From 27c1d4f1ec240bd3b9b41690bec94bfae6c812d0 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Thu, 25 Jul 2024 20:19:06 +0200 Subject: [PATCH] wip --- cmd/go.mod | 66 - create_dbs.sh | 37 - docker-compose.dev-refresh.yml | 58 - docker-compose.dev.yml | 28 - docker-compose.ext.yml | 17 - docker-compose.reputation.yml | 48 - docker-compose.yml | 324 --- ecs-vars-dev.env | 24 - tools/payments/cmd/disaster/go.mod => go.mod | 75 +- cmd/go.sum => go.sum | 237 +- libs/altcurrency/altcurrency.go | 161 -- libs/altcurrency/altcurrency_test.go | 116 - libs/aws/kms.go | 30 - libs/aws/mock/mock.go | 56 - libs/aws/s3.go | 54 - libs/backoff/backoff.go | 49 - libs/backoff/backoff_test.go | 128 - libs/backoff/retrypolicy/factory.go | 25 - libs/backoff/retrypolicy/mock/retrypolicy.go | 49 - libs/backoff/retrypolicy/retrypolicy.go | 136 - libs/backoff/retrypolicy/retrypolicy_test.go | 101 - libs/clients/bitflyer/client.go | 579 ---- libs/clients/bitflyer/client_test.go | 115 - libs/clients/bitflyer/instrumented_client.go | 134 - libs/clients/bitflyer/mock/mock.go | 138 - libs/clients/cbr/client.go | 270 -- libs/clients/cbr/client_test.go | 263 -- libs/clients/cbr/instrumented_client.go | 151 - libs/clients/cbr/mock/mock.go | 151 - libs/clients/client.go | 333 --- libs/clients/client_test.go | 44 - libs/clients/coingecko/client.go | 435 --- libs/clients/coingecko/client_test.go | 159 -- libs/clients/coingecko/instrumented_client.go | 109 - libs/clients/coingecko/mock/mock.go | 114 - libs/clients/errors.go | 55 - libs/clients/gemini/client.go | 478 ---- libs/clients/gemini/client_test.go | 132 - libs/clients/gemini/instrumented_client.go | 110 - libs/clients/gemini/mock/mock.go | 112 - libs/clients/radom/instrumented.go | 50 - libs/clients/radom/mock.go | 20 - libs/clients/radom/radom.go | 212 -- libs/clients/ratios/client.go | 141 - libs/clients/ratios/client_test.go | 33 - libs/clients/ratios/instrumented_client.go | 53 - libs/clients/ratios/mock/mock.go | 51 - libs/clients/reputation/client.go | 358 --- libs/clients/reputation/client_test.go | 21 - .../clients/reputation/instrumented_client.go | 139 - libs/clients/reputation/mock/mock.go | 142 - libs/clients/reputation/proxy.go | 34 - libs/clients/reputation/proxy_test.go | 33 - libs/clients/stripe/client.go | 144 - libs/clients/stripe/client_test.go | 94 - libs/clients/stripe/instrumented_client.go | 53 - libs/clients/stripe/mock/mock.go | 51 - libs/clients/zebpay/client.go | 334 --- libs/clients/zebpay/client_test.go | 73 - libs/clients/zebpay/instrumented_client.go | 82 - libs/clients/zebpay/mock/mock.go | 87 - libs/closers/log.go | 18 - libs/closers/panic.go | 29 - libs/concurrent/set.go | 32 - libs/concurrent/set_test.go | 21 - libs/contains/contains.go | 12 - libs/context/getters.go | 91 - libs/context/keys.go | 267 -- libs/context/wrap.go | 38 - libs/cryptography/attenutated_credential.go | 93 - libs/cryptography/attenutated_credential.md | 41 - .../attenutated_credential_test.go | 70 - libs/cryptography/encryption.go | 56 - libs/cryptography/encryption_test.go | 48 - libs/cryptography/hmac.go | 37 - libs/cryptography/presigner.go | 21 - libs/cryptography/time_limited.go | 39 - libs/cryptography/time_limited_test.go | 54 - libs/custodian/provider/bitflyer.go | 319 --- libs/custodian/provider/bitflyer_test.go | 128 - libs/custodian/provider/custodian.go | 138 - libs/custodian/provider/gemini.go | 42 - libs/custodian/provider/gemini_test.go | 94 - libs/custodian/provider/uphold.go | 31 - libs/custodian/regions.go | 166 -- libs/custodian/regions_test.go | 29 - libs/custodian/transaction.go | 84 - libs/datastore/models.go | 60 - libs/datastore/postgres.go | 242 -- libs/datastore/tx.go | 62 - libs/digest/digest.go | 79 - libs/digest/digest_test.go | 70 - libs/errors/errors.go | 214 -- libs/errors/errors_test.go | 84 - libs/errors/helpers.go | 64 - libs/formatters/cliformatter.go | 29 - libs/go.mod | 107 - libs/go.sum | 2049 -------------- libs/handlers/handlers.go | 161 -- libs/handlers/handlers_test.go | 68 - libs/handlers/healthcheck.go | 45 - libs/httpsignature/algorithm.go | 53 - libs/httpsignature/att_doc_sample.bin | Bin 4396 -> 0 bytes libs/httpsignature/ed25519.go | 34 - libs/httpsignature/encapsulate.go | 151 - libs/httpsignature/encapsulate_test.go | 119 - libs/httpsignature/hmac.go | 39 - libs/httpsignature/httpsignature.go | 441 --- libs/httpsignature/httpsignature_test.go | 743 ----- libs/httpsignature/nitro.go | 16 - libs/httpsignature/nitro_test.go | 57 - libs/inputs/decode.go | 23 - libs/inputs/ids.go | 54 - libs/inputs/inputs.go | 45 - libs/inputs/merchant.go | 54 - libs/inputs/pagination.go | 199 -- libs/inputs/pagination_test.go | 51 - libs/inputs/publickey.go | 39 - libs/inputs/validate.go | 13 - libs/jsonutils/jsonstringarray.go | 56 - libs/kafka/consumer.go | 66 - libs/kafka/dialer.go | 229 -- libs/kafka/instrument.go | 57 - libs/kafka/mock/dialer.go | 99 - libs/kv/kv.go | 46 - libs/kv/kv_test.go | 39 - libs/logging/logging.go | 246 -- libs/logging/logging_test.go | 45 - libs/metrics/db_stats.go | 185 -- libs/metrics/db_stats_test.go | 44 - libs/middleware/context.go | 18 - libs/middleware/date.go | 56 - libs/middleware/date_test.go | 52 - libs/middleware/http_signed.go | 94 - libs/middleware/http_signed_test.go | 147 - libs/middleware/prometheus.go | 211 -- libs/middleware/rate_limiter.go | 103 - libs/middleware/rate_limiter_test.go | 79 - libs/middleware/request_host.go | 20 - libs/middleware/request_id.go | 26 - libs/middleware/request_logger.go | 123 - libs/middleware/request_logger_test.go | 46 - libs/middleware/response_date.go | 16 - libs/middleware/simple_token.go | 64 - libs/middleware/simple_token_test.go | 41 - libs/middleware/upgrade_required.go | 31 - libs/middleware/upgrade_required_test.go | 26 - libs/nitro/attestation.go | 188 -- libs/nitro/aws/sdk.go | 164 -- libs/nitro/ber/ber.go | 301 -- libs/nitro/ber/ber_test.go | 212 -- libs/nitro/handlers.go | 14 - libs/nitro/kms.go | 23 - libs/nitro/kms_test.go | 36 - libs/nitro/log.go | 118 - libs/nitro/log_test.go | 31 - libs/nitro/pkcs7/decrypt.go | 194 -- libs/nitro/pkcs7/encrypt.go | 94 - libs/nitro/pkcs7/pkcs7.go | 105 - libs/nitro/vsock.go | 242 -- libs/passphrase/passphrase.go | 72 - libs/passphrase/passphrase_test.go | 220 -- libs/payments/api.go | 10 - libs/payments/crypto.go | 25 - libs/payments/errors.go | 51 - libs/payments/messages.go | 60 - libs/payments/requests.go | 84 - libs/payments/state.go | 192 -- libs/payments/state_test.go | 177 -- libs/payments/streams.go | 23 - libs/payments/transitions.go | 63 - libs/payments/transitions_test.go | 44 - libs/pindialer/dialer.go | 67 - libs/prompt/prompt.go | 38 - libs/ptr/ptr.go | 39 - libs/redisconsumer/consumer.go | 305 -- libs/redisconsumer/consumer_test.go | 89 - libs/redisconsumer/gen-test-certs.sh | 75 - libs/redisconsumer/privkey.pem | 0 libs/redisconsumer/tests/README.md | 9 - libs/redisconsumer/tests/docker-compose.yml | 20 - libs/redisconsumer/tests/tls/ca.crt | 31 - libs/redisconsumer/tests/tls/ca.key | 52 - libs/redisconsumer/tests/tls/ca.txt | 1 - libs/redisconsumer/tests/tls/openssl.cnf | 8 - libs/redisconsumer/tests/tls/server.cnf | 17 - libs/redisconsumer/tests/tls/server.crt | 26 - libs/redisconsumer/tests/tls/server.key | 28 - libs/redisconsumer/tests/tls/users.acl | 1 - libs/requestutils/requestutils.go | 77 - libs/responses/meta.go | 8 - libs/responses/pagination.go | 35 - libs/rootdir/main.go | 11 - libs/service/secrets.go | 10 - libs/service/service.go | 50 - libs/set/set.go | 93 - libs/set/set_test.go | 61 - libs/test/matcher.go | 28 - libs/test/random.go | 45 - libs/time/duration.go | 171 -- libs/time/duration_test.go | 127 - libs/useragent/useragent.go | 35 - libs/useragent/useragent_test.go | 20 - libs/validators/validators.go | 99 - libs/validators/validators_test.go | 110 - libs/wallet/provider/provider.go | 25 - libs/wallet/provider/uphold/errors.go | 141 - libs/wallet/provider/uphold/errors_test.go | 79 - libs/wallet/provider/uphold/uphold.go | 1125 -------- libs/wallet/provider/uphold/uphold_test.go | 350 --- libs/wallet/wallet.go | 91 - main/go.mod | 249 -- main/go.sum | 2470 ----------------- main/main.go | 28 +- services/cmd/serve.go | 4 +- services/go.mod | 149 - services/go.sum | 2357 ---------------- services/grant/cmd/grant.go | 724 ----- services/grant/cmd/grant_test.go | 31 - services/grant/datastore.go | 137 - services/grant/grant.go | 32 - services/grant/grant_test.go | 18 - services/grant/instrumented_datastore.go | 132 - .../grant/instrumented_read_only_datastore.go | 132 - services/grant/mockdatastore.go | 266 -- services/grant/service.go | 53 - services/nitro/nitro.go | 6 +- services/payments/cmd/payments.go | 4 +- services/payments/cmd/rest_run.go | 6 +- services/payments/cmd/worker.go | 4 +- services/promotion/README.md | 33 - services/promotion/avro.go | 53 - services/promotion/claim.go | 252 -- services/promotion/claim_summary.go | 15 - services/promotion/claim_test.go | 67 - services/promotion/controllers.go | 692 ----- services/promotion/controllers_test.go | 1288 --------- services/promotion/custodian.go | 20 - services/promotion/datastore.go | 1175 -------- services/promotion/datastore_test.go | 756 ----- services/promotion/drain.go | 97 - services/promotion/drain_info.go | 18 - services/promotion/drain_test.go | 1 - services/promotion/instrumented_datastore.go | 515 ---- .../instrumented_read_only_datastore.go | 274 -- services/promotion/issuer.go | 57 - services/promotion/mockclaim.go | 51 - services/promotion/mockdatastore.go | 822 ------ services/promotion/mockdrain.go | 5 - services/promotion/mockservice.go | 5 - services/promotion/order.go | 72 - services/promotion/promotion.go | 145 - services/promotion/promotion_test.go | 89 - services/promotion/service.go | 285 -- services/promotion/service_test.go | 109 - services/promotion/suggestions.go | 425 --- services/promotion/suggestions_test.go | 86 - services/ratios/cache.go | 185 -- services/ratios/cmd/ratios.go | 66 - services/ratios/cmd/rest_run.go | 99 - services/ratios/controllers.go | 451 --- services/ratios/controllers_test.go | 621 ----- services/ratios/inputs.go | 307 -- services/ratios/mapping.go | 219 -- services/ratios/middleware.go | 50 - services/ratios/service.go | 311 --- services/rewards/Dockerfile | 2 - services/rewards/README.md | 9 - services/rewards/choices.go | 99 - services/rewards/cmd/rest_run.go | 107 - services/rewards/cmd/rewards.go | 81 - services/rewards/controllers.go | 56 - services/rewards/controllers_test.go | 121 - services/rewards/docker-compose.yml | 63 - services/rewards/inputs.go | 252 -- .../example-parameters-response.json | 17 - .../example-parameters-response.schema | 47 - services/rewards/parameters.go | 30 - services/rewards/protobuf/rewards.proto | 246 -- services/rewards/service.go | 179 -- services/rewards/service_test.go | 7 - services/skus/Dockerfile | 2 - services/skus/README.md | 244 -- services/skus/avro.go | 242 -- services/skus/controllers.go | 1371 --------- services/skus/controllers_test.go | 1892 ------------- services/skus/credentials.go | 786 ------ services/skus/credentials_test.go | 659 ----- services/skus/datastore.go | 1442 ---------- services/skus/datastore_test.go | 666 ----- .../skus/docker-compose.payment-refresh.yml | 58 - services/skus/docker-compose.yml | 44 - services/skus/docs/StripeSKUsIntegration.pdf | Bin 45035 -> 0 bytes services/skus/docs/cross-platform.md | 69 - services/skus/handler/handler.go | 137 - services/skus/handler/handler_pvt_test.go | 146 - services/skus/handler/handler_test.go | 518 ---- services/skus/helpers.go | 1 - services/skus/input.go | 491 ---- services/skus/instrumented_datastore.go | 794 ------ services/skus/key.go | 232 -- services/skus/key_test.go | 480 ---- services/skus/mockcredentials.go | 141 - services/skus/mockdatastore.go | 1179 -------- services/skus/model/model.go | 645 ----- services/skus/model/model_pvt_test.go | 85 - services/skus/model/model_test.go | 712 ----- services/skus/order.go | 202 -- services/skus/order_test.go | 201 -- services/skus/receipt.go | 287 -- services/skus/response.go | 7 - services/skus/service.go | 1946 ------------- services/skus/service_nonint_test.go | 381 --- services/skus/service_test.go | 308 -- services/skus/skus.go | 125 - services/skus/skustest/skustest.go | 67 - services/skus/storage/repository/issuer.go | 60 - .../skus/storage/repository/issuer_test.go | 284 -- services/skus/storage/repository/mock.go | 78 - .../skus/storage/repository/order_history.go | 35 - .../skus/storage/repository/order_item.go | 81 - .../storage/repository/order_item_test.go | 223 -- .../skus/storage/repository/repository.go | 274 -- .../storage/repository/repository_test.go | 981 ------- services/skus/transaction.go | 21 - services/skus/vote.go | 336 --- services/skus/vote_test.go | 216 -- services/wallet/Dockerfile | 2 - services/wallet/README.md | 63 - services/wallet/cmd/rest_run.go | 55 - services/wallet/cmd/wallets.go | 58 - services/wallet/controllers_v3.go | 715 ----- services/wallet/controllers_v3_test.go | 1135 -------- services/wallet/controllers_v4.go | 178 -- services/wallet/controllers_v4_test.go | 566 ---- services/wallet/datastore.go | 1045 ------- services/wallet/datastore_pvt_test.go | 59 - services/wallet/datastore_test.go | 418 --- services/wallet/docker-compose.yml | 38 - services/wallet/gemini.go | 72 - services/wallet/gemini_test.go | 246 -- services/wallet/geocountry.go | 65 - services/wallet/geocountry_test.go | 93 - services/wallet/inputs.go | 482 ---- services/wallet/instrumented_datastore.go | 360 --- .../instrumented_read_only_datastore.go | 204 -- services/wallet/keystore.go | 59 - services/wallet/keystore_test.go | 530 ---- services/wallet/metric/metric.go | 92 - services/wallet/mockservice.go | 97 - services/wallet/model/model.go | 11 - services/wallet/outputs.go | 187 -- services/wallet/service.go | 942 ------- services/wallet/service_test.go | 311 --- services/wallet/wallettest/wallettest.go | 40 - tools/Dockerfile | 35 - tools/Makefile | 73 - tools/cmd/README.md | 236 -- tools/cmd/generate.go | 95 - tools/cmd/get-cert-fingerprint.go | 63 - tools/cmd/schema.go | 19 - tools/go.mod | 221 -- tools/go.sum | 2396 ---------------- tools/macaroon/cmd/.gitignore | 1 - tools/macaroon/cmd/README.md | 33 - tools/macaroon/cmd/ac_skus.yaml | 20 - .../premium_dev_time-limited.yaml | 21 - .../premium_dev_time_limited_v2.yaml | 22 - .../premium_dev_time_limited_v2_bat.yaml | 20 - .../premium_prod_time-limited.yaml | 20 - .../premium_prod_time_limited_v2.yaml | 24 - .../premium_prod_time_limited_v2_bat.yaml | 22 - .../premium_stg_time-limited.yaml | 21 - .../premium_stg_time_limited_v2.yaml | 22 - .../premium_stg_time_limited_v2_bat.yaml | 20 - .../premium_development_time_limited_v2.yaml | 22 - ...um_development_yearly_time_limited_v2.yaml | 22 - .../premium_prod_time_limited_v2.yaml | 23 - .../premium_prod_yearly_time_limited_v2.yaml | 23 - .../premium_stg_time_limited_v2.yaml | 22 - .../premium_stg_yearly_time_limited_v2.yaml | 22 - .../development-captcha-single-use.yaml | 10 - .../development-captcha-time-limited.yaml | 12 - .../premium_dev_time-limited.yaml | 22 - .../premium_prod_time-limited.yaml | 22 - .../premium_stg_time-limited.yaml | 22 - .../premium_year_dev_time-limited.yaml | 22 - .../premium_year_prod_time-limited.yaml | 22 - .../premium_year_stg_time-limited.yaml | 22 - .../production-captcha-single-use.yaml | 10 - .../staging-captcha-single-use.yaml | 10 - .../macaroon/cmd/brave-skus/free_1m_tlv2.yaml | 13 - .../macaroon/cmd/brave-skus/free_5m_tlv2.yaml | 13 - tools/macaroon/cmd/brave-skus/free_dev.yaml | 13 - .../cmd/brave-skus/free_nocc_dev.yaml | 9 - .../cmd/brave-skus/free_nocc_stg.yaml | 9 - .../cmd/brave-skus/free_premium_dev.yaml | 13 - .../cmd/brave-skus/free_premium_stg.yaml | 13 - tools/macaroon/cmd/brave-skus/free_stg.yaml | 13 - .../cmd/brave-skus/free_unlimited_dev.yaml | 14 - .../cmd/brave-skus/free_unlimitted_stg.yaml | 14 - .../macaroon/cmd/brave-skus/premium_dev.yaml | 13 - .../macaroon/cmd/brave-skus/premium_stg.yaml | 13 - .../cmd/brave-skus/unlimited_dev.yaml | 13 - .../cmd/brave-skus/unlimited_stg.yaml | 13 - .../cmd/brave-talk/brave_talk_free_dev.yaml | 13 - .../cmd/brave-talk/brave_talk_paid_dev.yaml | 18 - .../brave-talk/premium_dev_time-limited.yaml | 21 - .../premium_dev_time-limited_beta.yaml | 21 - .../brave-talk/premium_prod_time-limited.yaml | 19 - .../premium_prod_time-limited_beta.yaml | 19 - .../brave-talk/premium_stg_time-limited.yaml | 22 - .../premium_stg_time-limited_beta.yaml | 21 - .../brave_together_free_dev.yaml | 11 - .../brave_together_free_prod.yaml | 11 - .../brave_together_free_staging.yaml | 11 - .../brave_together_paid_dev.yaml | 12 - .../brave_together_paid_prod.yaml | 12 - .../brave_together_paid_staging.yaml | 12 - tools/macaroon/cmd/example.yaml | 30 - tools/macaroon/cmd/free_example.yaml | 10 - tools/macaroon/cmd/gen.go | 100 - tools/macaroon/cmd/search_closed_beta.yaml | 10 - tools/macaroon/cmd/token.go | 76 - .../cmd/webtest-pj.herokuapp.com.yaml | 10 - tools/merchant/cmd/README.md | 17 - tools/merchant/cmd/new_keys.go | 154 - tools/payments/cmd/authorize/go.mod | 52 - tools/payments/cmd/authorize/go.sum | 135 - tools/payments/cmd/authorize/main.go | 2 +- tools/payments/cmd/bootstrap/go.mod | 73 - tools/payments/cmd/bootstrap/go.sum | 188 -- tools/payments/cmd/bootstrap/main.go | 2 +- tools/payments/cmd/chain-account/go.mod | 52 - tools/payments/cmd/chain-account/go.sum | 135 - tools/payments/cmd/chain-account/main.go | 2 +- tools/payments/cmd/configure/go.mod | 36 - tools/payments/cmd/configure/go.sum | 84 - tools/payments/cmd/disaster/go.sum | 192 -- tools/payments/cmd/disaster/main.go | 2 +- tools/payments/cmd/prepare/go.mod | 52 - tools/payments/cmd/prepare/go.sum | 135 - tools/payments/cmd/prepare/main.go | 2 +- tools/payments/cmd/report/go.mod | 52 - tools/payments/cmd/report/go.sum | 135 - tools/payments/cmd/report/main.go | 2 +- tools/payments/cmd/status/go.mod | 50 - tools/payments/cmd/status/go.sum | 565 ---- tools/payments/cmd/status/main.go | 2 +- tools/payments/cmd/validate/go.mod | 50 - tools/payments/cmd/validate/go.sum | 135 - tools/payments/cmd/validate/main.go | 2 +- tools/payments/cmd/vault/go.mod | 57 - tools/payments/cmd/vault/go.sum | 577 ---- tools/payments/cmd/vault/main.go | 2 +- tools/payments/go.mod | 49 - tools/payments/go.sum | 554 ---- tools/payments/report_test.go | 2 +- ...p4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc | Bin 0 -> 312 bytes ...p4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc | Bin 0 -> 312 bytes tools/settlement/README.md | 182 -- tools/settlement/bitflyer/upload.go | 464 ---- tools/settlement/bitflyer/upload_mock_test.go | 652 ----- tools/settlement/bitflyer/upload_test.go | 344 --- tools/settlement/cmd/bitflyer.go | 318 --- tools/settlement/cmd/gemini.go | 223 -- tools/settlement/cmd/paypal.go | 323 --- tools/settlement/cmd/settlement.go | 63 - tools/settlement/cmd/uphold.go | 361 --- tools/settlement/config.example.yaml | 6 - tools/settlement/config.go | 50 - tools/settlement/config.hcl | 8 - tools/settlement/files.go | 26 - tools/settlement/gemini/sign.go | 62 - tools/settlement/gemini/upload.go | 346 --- tools/settlement/hashicorp.asc | 122 - tools/settlement/paypal/data.go | 94 - tools/settlement/paypal/transform.go | 84 - tools/settlement/settlement.go | 451 --- tools/settlement/settlement_test.go | 357 --- tools/settlement/uphold/prepare.go | 49 - tools/settlement/uphold/prepare_test.go | 151 - tools/vault/cmd/create_wallet.go | 194 -- tools/vault/cmd/import_key.go | 220 -- tools/vault/cmd/init.go | 200 -- tools/vault/cmd/sign_settlement.go | 516 ---- tools/vault/cmd/unseal.go | 84 - tools/vault/cmd/vault.go | 33 - tools/vault/signer/ed25519signer.go | 71 - tools/vault/signer/hmacsigner.go | 29 - tools/vault/signer/hmacsigner_test.go | 51 - tools/vault/signer/vaultsigner.go | 227 -- tools/vault/signer/vaultsigner_test.go | 78 - tools/wallet/cmd/create.go | 105 - tools/wallet/cmd/list_transactions.go | 190 -- tools/wallet/cmd/transfer_funds.go | 366 --- 496 files changed, 278 insertions(+), 83778 deletions(-) delete mode 100644 cmd/go.mod delete mode 100755 create_dbs.sh delete mode 100644 docker-compose.dev-refresh.yml delete mode 100644 docker-compose.dev.yml delete mode 100644 docker-compose.ext.yml delete mode 100644 docker-compose.reputation.yml delete mode 100644 docker-compose.yml delete mode 100644 ecs-vars-dev.env rename tools/payments/cmd/disaster/go.mod => go.mod (51%) rename cmd/go.sum => go.sum (76%) delete mode 100644 libs/altcurrency/altcurrency.go delete mode 100644 libs/altcurrency/altcurrency_test.go delete mode 100644 libs/aws/kms.go delete mode 100644 libs/aws/mock/mock.go delete mode 100644 libs/aws/s3.go delete mode 100644 libs/backoff/backoff.go delete mode 100644 libs/backoff/backoff_test.go delete mode 100644 libs/backoff/retrypolicy/factory.go delete mode 100644 libs/backoff/retrypolicy/mock/retrypolicy.go delete mode 100644 libs/backoff/retrypolicy/retrypolicy.go delete mode 100644 libs/backoff/retrypolicy/retrypolicy_test.go delete mode 100644 libs/clients/bitflyer/client.go delete mode 100644 libs/clients/bitflyer/client_test.go delete mode 100755 libs/clients/bitflyer/instrumented_client.go delete mode 100644 libs/clients/bitflyer/mock/mock.go delete mode 100644 libs/clients/cbr/client.go delete mode 100644 libs/clients/cbr/client_test.go delete mode 100755 libs/clients/cbr/instrumented_client.go delete mode 100644 libs/clients/cbr/mock/mock.go delete mode 100644 libs/clients/client.go delete mode 100644 libs/clients/client_test.go delete mode 100644 libs/clients/coingecko/client.go delete mode 100644 libs/clients/coingecko/client_test.go delete mode 100755 libs/clients/coingecko/instrumented_client.go delete mode 100644 libs/clients/coingecko/mock/mock.go delete mode 100644 libs/clients/errors.go delete mode 100644 libs/clients/gemini/client.go delete mode 100644 libs/clients/gemini/client_test.go delete mode 100755 libs/clients/gemini/instrumented_client.go delete mode 100644 libs/clients/gemini/mock/mock.go delete mode 100644 libs/clients/radom/instrumented.go delete mode 100644 libs/clients/radom/mock.go delete mode 100644 libs/clients/radom/radom.go delete mode 100644 libs/clients/ratios/client.go delete mode 100644 libs/clients/ratios/client_test.go delete mode 100755 libs/clients/ratios/instrumented_client.go delete mode 100644 libs/clients/ratios/mock/mock.go delete mode 100644 libs/clients/reputation/client.go delete mode 100644 libs/clients/reputation/client_test.go delete mode 100755 libs/clients/reputation/instrumented_client.go delete mode 100644 libs/clients/reputation/mock/mock.go delete mode 100644 libs/clients/reputation/proxy.go delete mode 100644 libs/clients/reputation/proxy_test.go delete mode 100644 libs/clients/stripe/client.go delete mode 100644 libs/clients/stripe/client_test.go delete mode 100644 libs/clients/stripe/instrumented_client.go delete mode 100644 libs/clients/stripe/mock/mock.go delete mode 100644 libs/clients/zebpay/client.go delete mode 100644 libs/clients/zebpay/client_test.go delete mode 100644 libs/clients/zebpay/instrumented_client.go delete mode 100644 libs/clients/zebpay/mock/mock.go delete mode 100644 libs/closers/log.go delete mode 100644 libs/closers/panic.go delete mode 100644 libs/concurrent/set.go delete mode 100644 libs/concurrent/set_test.go delete mode 100644 libs/contains/contains.go delete mode 100644 libs/context/getters.go delete mode 100644 libs/context/keys.go delete mode 100644 libs/context/wrap.go delete mode 100644 libs/cryptography/attenutated_credential.go delete mode 100644 libs/cryptography/attenutated_credential.md delete mode 100644 libs/cryptography/attenutated_credential_test.go delete mode 100644 libs/cryptography/encryption.go delete mode 100644 libs/cryptography/encryption_test.go delete mode 100644 libs/cryptography/hmac.go delete mode 100644 libs/cryptography/presigner.go delete mode 100644 libs/cryptography/time_limited.go delete mode 100644 libs/cryptography/time_limited_test.go delete mode 100644 libs/custodian/provider/bitflyer.go delete mode 100644 libs/custodian/provider/bitflyer_test.go delete mode 100644 libs/custodian/provider/custodian.go delete mode 100644 libs/custodian/provider/gemini.go delete mode 100644 libs/custodian/provider/gemini_test.go delete mode 100644 libs/custodian/provider/uphold.go delete mode 100644 libs/custodian/regions.go delete mode 100644 libs/custodian/regions_test.go delete mode 100644 libs/custodian/transaction.go delete mode 100644 libs/datastore/models.go delete mode 100644 libs/datastore/postgres.go delete mode 100644 libs/datastore/tx.go delete mode 100644 libs/digest/digest.go delete mode 100644 libs/digest/digest_test.go delete mode 100644 libs/errors/errors.go delete mode 100644 libs/errors/errors_test.go delete mode 100644 libs/errors/helpers.go delete mode 100644 libs/formatters/cliformatter.go delete mode 100644 libs/go.mod delete mode 100644 libs/go.sum delete mode 100644 libs/handlers/handlers.go delete mode 100644 libs/handlers/handlers_test.go delete mode 100644 libs/handlers/healthcheck.go delete mode 100644 libs/httpsignature/algorithm.go delete mode 100644 libs/httpsignature/att_doc_sample.bin delete mode 100644 libs/httpsignature/ed25519.go delete mode 100644 libs/httpsignature/encapsulate.go delete mode 100644 libs/httpsignature/encapsulate_test.go delete mode 100644 libs/httpsignature/hmac.go delete mode 100644 libs/httpsignature/httpsignature.go delete mode 100644 libs/httpsignature/httpsignature_test.go delete mode 100644 libs/httpsignature/nitro.go delete mode 100644 libs/httpsignature/nitro_test.go delete mode 100644 libs/inputs/decode.go delete mode 100644 libs/inputs/ids.go delete mode 100644 libs/inputs/inputs.go delete mode 100644 libs/inputs/merchant.go delete mode 100644 libs/inputs/pagination.go delete mode 100644 libs/inputs/pagination_test.go delete mode 100644 libs/inputs/publickey.go delete mode 100644 libs/inputs/validate.go delete mode 100644 libs/jsonutils/jsonstringarray.go delete mode 100644 libs/kafka/consumer.go delete mode 100644 libs/kafka/dialer.go delete mode 100644 libs/kafka/instrument.go delete mode 100644 libs/kafka/mock/dialer.go delete mode 100644 libs/kv/kv.go delete mode 100644 libs/kv/kv_test.go delete mode 100644 libs/logging/logging.go delete mode 100644 libs/logging/logging_test.go delete mode 100644 libs/metrics/db_stats.go delete mode 100644 libs/metrics/db_stats_test.go delete mode 100644 libs/middleware/context.go delete mode 100644 libs/middleware/date.go delete mode 100644 libs/middleware/date_test.go delete mode 100644 libs/middleware/http_signed.go delete mode 100644 libs/middleware/http_signed_test.go delete mode 100644 libs/middleware/prometheus.go delete mode 100644 libs/middleware/rate_limiter.go delete mode 100644 libs/middleware/rate_limiter_test.go delete mode 100644 libs/middleware/request_host.go delete mode 100644 libs/middleware/request_id.go delete mode 100644 libs/middleware/request_logger.go delete mode 100644 libs/middleware/request_logger_test.go delete mode 100644 libs/middleware/response_date.go delete mode 100644 libs/middleware/simple_token.go delete mode 100644 libs/middleware/simple_token_test.go delete mode 100644 libs/middleware/upgrade_required.go delete mode 100644 libs/middleware/upgrade_required_test.go delete mode 100644 libs/nitro/attestation.go delete mode 100644 libs/nitro/aws/sdk.go delete mode 100644 libs/nitro/ber/ber.go delete mode 100644 libs/nitro/ber/ber_test.go delete mode 100644 libs/nitro/handlers.go delete mode 100644 libs/nitro/kms.go delete mode 100644 libs/nitro/kms_test.go delete mode 100644 libs/nitro/log.go delete mode 100644 libs/nitro/log_test.go delete mode 100644 libs/nitro/pkcs7/decrypt.go delete mode 100644 libs/nitro/pkcs7/encrypt.go delete mode 100644 libs/nitro/pkcs7/pkcs7.go delete mode 100644 libs/nitro/vsock.go delete mode 100644 libs/passphrase/passphrase.go delete mode 100644 libs/passphrase/passphrase_test.go delete mode 100644 libs/payments/api.go delete mode 100644 libs/payments/crypto.go delete mode 100644 libs/payments/errors.go delete mode 100644 libs/payments/messages.go delete mode 100644 libs/payments/requests.go delete mode 100644 libs/payments/state.go delete mode 100644 libs/payments/state_test.go delete mode 100644 libs/payments/streams.go delete mode 100644 libs/payments/transitions.go delete mode 100644 libs/payments/transitions_test.go delete mode 100644 libs/pindialer/dialer.go delete mode 100644 libs/prompt/prompt.go delete mode 100644 libs/ptr/ptr.go delete mode 100644 libs/redisconsumer/consumer.go delete mode 100644 libs/redisconsumer/consumer_test.go delete mode 100755 libs/redisconsumer/gen-test-certs.sh delete mode 100644 libs/redisconsumer/privkey.pem delete mode 100644 libs/redisconsumer/tests/README.md delete mode 100644 libs/redisconsumer/tests/docker-compose.yml delete mode 100644 libs/redisconsumer/tests/tls/ca.crt delete mode 100644 libs/redisconsumer/tests/tls/ca.key delete mode 100644 libs/redisconsumer/tests/tls/ca.txt delete mode 100644 libs/redisconsumer/tests/tls/openssl.cnf delete mode 100644 libs/redisconsumer/tests/tls/server.cnf delete mode 100644 libs/redisconsumer/tests/tls/server.crt delete mode 100644 libs/redisconsumer/tests/tls/server.key delete mode 100644 libs/redisconsumer/tests/tls/users.acl delete mode 100644 libs/requestutils/requestutils.go delete mode 100644 libs/responses/meta.go delete mode 100644 libs/responses/pagination.go delete mode 100644 libs/rootdir/main.go delete mode 100644 libs/service/secrets.go delete mode 100644 libs/service/service.go delete mode 100644 libs/set/set.go delete mode 100644 libs/set/set_test.go delete mode 100644 libs/test/matcher.go delete mode 100644 libs/test/random.go delete mode 100644 libs/time/duration.go delete mode 100644 libs/time/duration_test.go delete mode 100644 libs/useragent/useragent.go delete mode 100644 libs/useragent/useragent_test.go delete mode 100644 libs/validators/validators.go delete mode 100644 libs/validators/validators_test.go delete mode 100644 libs/wallet/provider/provider.go delete mode 100644 libs/wallet/provider/uphold/errors.go delete mode 100644 libs/wallet/provider/uphold/errors_test.go delete mode 100644 libs/wallet/provider/uphold/uphold.go delete mode 100644 libs/wallet/provider/uphold/uphold_test.go delete mode 100644 libs/wallet/wallet.go delete mode 100644 main/go.mod delete mode 100644 main/go.sum delete mode 100644 services/go.mod delete mode 100644 services/go.sum delete mode 100644 services/grant/cmd/grant.go delete mode 100644 services/grant/cmd/grant_test.go delete mode 100644 services/grant/datastore.go delete mode 100644 services/grant/grant.go delete mode 100644 services/grant/grant_test.go delete mode 100755 services/grant/instrumented_datastore.go delete mode 100644 services/grant/instrumented_read_only_datastore.go delete mode 100644 services/grant/mockdatastore.go delete mode 100644 services/grant/service.go delete mode 100644 services/promotion/README.md delete mode 100644 services/promotion/avro.go delete mode 100644 services/promotion/claim.go delete mode 100644 services/promotion/claim_summary.go delete mode 100644 services/promotion/claim_test.go delete mode 100644 services/promotion/controllers.go delete mode 100644 services/promotion/controllers_test.go delete mode 100644 services/promotion/custodian.go delete mode 100644 services/promotion/datastore.go delete mode 100644 services/promotion/datastore_test.go delete mode 100644 services/promotion/drain.go delete mode 100644 services/promotion/drain_info.go delete mode 100644 services/promotion/drain_test.go delete mode 100755 services/promotion/instrumented_datastore.go delete mode 100644 services/promotion/instrumented_read_only_datastore.go delete mode 100644 services/promotion/issuer.go delete mode 100644 services/promotion/mockclaim.go delete mode 100644 services/promotion/mockdatastore.go delete mode 100644 services/promotion/mockdrain.go delete mode 100644 services/promotion/mockservice.go delete mode 100644 services/promotion/order.go delete mode 100644 services/promotion/promotion.go delete mode 100644 services/promotion/promotion_test.go delete mode 100644 services/promotion/service.go delete mode 100644 services/promotion/service_test.go delete mode 100644 services/promotion/suggestions.go delete mode 100644 services/promotion/suggestions_test.go delete mode 100644 services/ratios/cache.go delete mode 100644 services/ratios/cmd/ratios.go delete mode 100644 services/ratios/cmd/rest_run.go delete mode 100644 services/ratios/controllers.go delete mode 100644 services/ratios/controllers_test.go delete mode 100644 services/ratios/inputs.go delete mode 100644 services/ratios/mapping.go delete mode 100644 services/ratios/middleware.go delete mode 100644 services/ratios/service.go delete mode 100644 services/rewards/Dockerfile delete mode 100644 services/rewards/README.md delete mode 100644 services/rewards/choices.go delete mode 100644 services/rewards/cmd/rest_run.go delete mode 100644 services/rewards/cmd/rewards.go delete mode 100644 services/rewards/controllers.go delete mode 100644 services/rewards/controllers_test.go delete mode 100644 services/rewards/docker-compose.yml delete mode 100644 services/rewards/inputs.go delete mode 100644 services/rewards/json-schema/example-parameters-response.json delete mode 100644 services/rewards/json-schema/example-parameters-response.schema delete mode 100644 services/rewards/parameters.go delete mode 100644 services/rewards/protobuf/rewards.proto delete mode 100644 services/rewards/service.go delete mode 100644 services/rewards/service_test.go delete mode 100644 services/skus/Dockerfile delete mode 100644 services/skus/README.md delete mode 100644 services/skus/avro.go delete mode 100644 services/skus/controllers.go delete mode 100644 services/skus/controllers_test.go delete mode 100644 services/skus/credentials.go delete mode 100644 services/skus/credentials_test.go delete mode 100644 services/skus/datastore.go delete mode 100644 services/skus/datastore_test.go delete mode 100644 services/skus/docker-compose.payment-refresh.yml delete mode 100644 services/skus/docker-compose.yml delete mode 100644 services/skus/docs/StripeSKUsIntegration.pdf delete mode 100644 services/skus/docs/cross-platform.md delete mode 100644 services/skus/handler/handler.go delete mode 100644 services/skus/handler/handler_pvt_test.go delete mode 100644 services/skus/handler/handler_test.go delete mode 100644 services/skus/helpers.go delete mode 100644 services/skus/input.go delete mode 100644 services/skus/instrumented_datastore.go delete mode 100644 services/skus/key.go delete mode 100644 services/skus/key_test.go delete mode 100644 services/skus/mockcredentials.go delete mode 100644 services/skus/mockdatastore.go delete mode 100644 services/skus/model/model.go delete mode 100644 services/skus/model/model_pvt_test.go delete mode 100644 services/skus/model/model_test.go delete mode 100644 services/skus/order.go delete mode 100644 services/skus/order_test.go delete mode 100644 services/skus/receipt.go delete mode 100644 services/skus/response.go delete mode 100644 services/skus/service.go delete mode 100644 services/skus/service_nonint_test.go delete mode 100644 services/skus/service_test.go delete mode 100644 services/skus/skus.go delete mode 100644 services/skus/skustest/skustest.go delete mode 100644 services/skus/storage/repository/issuer.go delete mode 100644 services/skus/storage/repository/issuer_test.go delete mode 100644 services/skus/storage/repository/mock.go delete mode 100644 services/skus/storage/repository/order_history.go delete mode 100644 services/skus/storage/repository/order_item.go delete mode 100644 services/skus/storage/repository/order_item_test.go delete mode 100644 services/skus/storage/repository/repository.go delete mode 100644 services/skus/storage/repository/repository_test.go delete mode 100644 services/skus/transaction.go delete mode 100644 services/skus/vote.go delete mode 100644 services/skus/vote_test.go delete mode 100644 services/wallet/Dockerfile delete mode 100644 services/wallet/README.md delete mode 100644 services/wallet/cmd/rest_run.go delete mode 100644 services/wallet/cmd/wallets.go delete mode 100644 services/wallet/controllers_v3.go delete mode 100644 services/wallet/controllers_v3_test.go delete mode 100644 services/wallet/controllers_v4.go delete mode 100644 services/wallet/controllers_v4_test.go delete mode 100644 services/wallet/datastore.go delete mode 100644 services/wallet/datastore_pvt_test.go delete mode 100644 services/wallet/datastore_test.go delete mode 100644 services/wallet/docker-compose.yml delete mode 100644 services/wallet/gemini.go delete mode 100644 services/wallet/gemini_test.go delete mode 100644 services/wallet/geocountry.go delete mode 100644 services/wallet/geocountry_test.go delete mode 100644 services/wallet/inputs.go delete mode 100644 services/wallet/instrumented_datastore.go delete mode 100755 services/wallet/instrumented_read_only_datastore.go delete mode 100644 services/wallet/keystore.go delete mode 100644 services/wallet/keystore_test.go delete mode 100644 services/wallet/metric/metric.go delete mode 100644 services/wallet/mockservice.go delete mode 100644 services/wallet/model/model.go delete mode 100644 services/wallet/outputs.go delete mode 100644 services/wallet/service.go delete mode 100644 services/wallet/service_test.go delete mode 100644 services/wallet/wallettest/wallettest.go delete mode 100644 tools/Dockerfile delete mode 100644 tools/Makefile delete mode 100644 tools/cmd/README.md delete mode 100644 tools/cmd/generate.go delete mode 100644 tools/cmd/get-cert-fingerprint.go delete mode 100644 tools/cmd/schema.go delete mode 100644 tools/go.mod delete mode 100644 tools/go.sum delete mode 100644 tools/macaroon/cmd/.gitignore delete mode 100644 tools/macaroon/cmd/README.md delete mode 100644 tools/macaroon/cmd/ac_skus.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2_bat.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2_bat.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2_bat.yaml delete mode 100644 tools/macaroon/cmd/brave-leo/premium_development_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-leo/premium_development_yearly_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-leo/premium_prod_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-leo/premium_prod_yearly_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-leo/premium_stg_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-leo/premium_stg_yearly_time_limited_v2.yaml delete mode 100644 tools/macaroon/cmd/brave-search/development-captcha-single-use.yaml delete mode 100644 tools/macaroon/cmd/brave-search/development-captcha-time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/premium_dev_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/premium_prod_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/premium_stg_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/premium_year_dev_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/premium_year_prod_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/premium_year_stg_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-search/production-captcha-single-use.yaml delete mode 100644 tools/macaroon/cmd/brave-search/staging-captcha-single-use.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_1m_tlv2.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_5m_tlv2.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_nocc_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_nocc_stg.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_premium_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_premium_stg.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_stg.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_unlimited_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/free_unlimitted_stg.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/premium_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/premium_stg.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/unlimited_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-skus/unlimited_stg.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/brave_talk_free_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/brave_talk_paid_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/premium_dev_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/premium_dev_time-limited_beta.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/premium_prod_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/premium_prod_time-limited_beta.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/premium_stg_time-limited.yaml delete mode 100644 tools/macaroon/cmd/brave-talk/premium_stg_time-limited_beta.yaml delete mode 100644 tools/macaroon/cmd/brave-together/brave_together_free_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-together/brave_together_free_prod.yaml delete mode 100644 tools/macaroon/cmd/brave-together/brave_together_free_staging.yaml delete mode 100644 tools/macaroon/cmd/brave-together/brave_together_paid_dev.yaml delete mode 100644 tools/macaroon/cmd/brave-together/brave_together_paid_prod.yaml delete mode 100644 tools/macaroon/cmd/brave-together/brave_together_paid_staging.yaml delete mode 100644 tools/macaroon/cmd/example.yaml delete mode 100644 tools/macaroon/cmd/free_example.yaml delete mode 100644 tools/macaroon/cmd/gen.go delete mode 100644 tools/macaroon/cmd/search_closed_beta.yaml delete mode 100644 tools/macaroon/cmd/token.go delete mode 100644 tools/macaroon/cmd/webtest-pj.herokuapp.com.yaml delete mode 100644 tools/merchant/cmd/README.md delete mode 100644 tools/merchant/cmd/new_keys.go delete mode 100644 tools/payments/cmd/authorize/go.mod delete mode 100644 tools/payments/cmd/authorize/go.sum delete mode 100644 tools/payments/cmd/bootstrap/go.mod delete mode 100644 tools/payments/cmd/bootstrap/go.sum delete mode 100644 tools/payments/cmd/chain-account/go.mod delete mode 100644 tools/payments/cmd/chain-account/go.sum delete mode 100644 tools/payments/cmd/configure/go.mod delete mode 100644 tools/payments/cmd/configure/go.sum delete mode 100644 tools/payments/cmd/disaster/go.sum delete mode 100644 tools/payments/cmd/prepare/go.mod delete mode 100644 tools/payments/cmd/prepare/go.sum delete mode 100644 tools/payments/cmd/report/go.mod delete mode 100644 tools/payments/cmd/report/go.sum delete mode 100644 tools/payments/cmd/status/go.mod delete mode 100644 tools/payments/cmd/status/go.sum delete mode 100644 tools/payments/cmd/validate/go.mod delete mode 100644 tools/payments/cmd/validate/go.sum delete mode 100644 tools/payments/cmd/vault/go.mod delete mode 100644 tools/payments/cmd/vault/go.sum delete mode 100644 tools/payments/go.mod delete mode 100644 tools/payments/go.sum create mode 100644 tools/payments/share-test-operator-age1eqa6hw3zjvazdvflh4gpp4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc create mode 100644 tools/payments/share-test-operator2-age1eqa6hw3zjvazdvflh4gpp4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc delete mode 100644 tools/settlement/README.md delete mode 100644 tools/settlement/bitflyer/upload.go delete mode 100644 tools/settlement/bitflyer/upload_mock_test.go delete mode 100644 tools/settlement/bitflyer/upload_test.go delete mode 100644 tools/settlement/cmd/bitflyer.go delete mode 100644 tools/settlement/cmd/gemini.go delete mode 100644 tools/settlement/cmd/paypal.go delete mode 100644 tools/settlement/cmd/settlement.go delete mode 100644 tools/settlement/cmd/uphold.go delete mode 100644 tools/settlement/config.example.yaml delete mode 100644 tools/settlement/config.go delete mode 100644 tools/settlement/config.hcl delete mode 100644 tools/settlement/files.go delete mode 100644 tools/settlement/gemini/sign.go delete mode 100644 tools/settlement/gemini/upload.go delete mode 100644 tools/settlement/hashicorp.asc delete mode 100644 tools/settlement/paypal/data.go delete mode 100644 tools/settlement/paypal/transform.go delete mode 100644 tools/settlement/settlement.go delete mode 100644 tools/settlement/settlement_test.go delete mode 100644 tools/settlement/uphold/prepare.go delete mode 100644 tools/settlement/uphold/prepare_test.go delete mode 100644 tools/vault/cmd/create_wallet.go delete mode 100644 tools/vault/cmd/import_key.go delete mode 100644 tools/vault/cmd/init.go delete mode 100644 tools/vault/cmd/sign_settlement.go delete mode 100644 tools/vault/cmd/unseal.go delete mode 100644 tools/vault/cmd/vault.go delete mode 100644 tools/vault/signer/ed25519signer.go delete mode 100644 tools/vault/signer/hmacsigner.go delete mode 100644 tools/vault/signer/hmacsigner_test.go delete mode 100644 tools/vault/signer/vaultsigner.go delete mode 100644 tools/vault/signer/vaultsigner_test.go delete mode 100644 tools/wallet/cmd/create.go delete mode 100644 tools/wallet/cmd/list_transactions.go delete mode 100644 tools/wallet/cmd/transfer_funds.go diff --git a/cmd/go.mod b/cmd/go.mod deleted file mode 100644 index 4c3c69c30..000000000 --- a/cmd/go.mod +++ /dev/null @@ -1,66 +0,0 @@ -module github.com/brave-intl/bat-go/cmd - -go 1.18 - -replace github.com/brave-intl/bat-go/services => ../services - -replace github.com/brave-intl/bat-go/libs => ../libs - -replace github.com/brave-intl/bat-go/tools => ../tools - -require ( - github.com/brave-intl/bat-go/libs v0.0.0-20220913154833-730f36b772de - github.com/rs/zerolog v1.28.0 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.13.0 -) - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/fxamacker/cbor/v2 v2.2.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/create_dbs.sh b/create_dbs.sh deleted file mode 100755 index 63bf67e1b..000000000 --- a/create_dbs.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -function create_database_and_user() { - local database=$1 - local user=$2 - local password=$3 - - echo "Creating database with user: $database $user" - psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL -CREATE USER $user WITH PASSWORD '$password'; -CREATE DATABASE $database; -GRANT ALL PRIVILEGES ON DATABASE $database TO $user; -EOSQL -} - -if [ -n $POSTGRES_EXTRA_DATABASES ]; then - echo "Creating multiple databases and users: $POSTGRES_EXTRA_DATABASES" - for dup in $(echo $POSTGRES_EXTRA_DATABASES | tr ',' ' '); do - db=$(echo $dup | awk -F":" '{print $1}') - user=$(echo $dup | awk -F":" '{print $2}') - password=$(echo $dup | awk -F":" '{print $3}') - - if [ -z "$user"]; then - user=$db - fi - - if [ -z "$password" ]; then - password=$user - fi - - create_database_and_user $db $user $password - done - - echo "Created multiple databases" -fi diff --git a/docker-compose.dev-refresh.yml b/docker-compose.dev-refresh.yml deleted file mode 100644 index ca5c07ea3..000000000 --- a/docker-compose.dev-refresh.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: "3.4" - -services: - # dev-refresh service will start up a grant server bound to host port 3334 - # which allows one to do `docker restart grant-dev-refresh` when the user - # wants to "restart" the service running new code. This is especially helpful - # when you hook it up to `fswatch` type utilities, causing a re-run of `go run` - # every time a file changes. - dev-refresh: - container_name: grant-dev-refresh - image: golang:1.19 - ports: - - "3335:3333" - - "6061:6061" - security_opt: - - no-new-privileges:true - read_only: true - command: "cd main && go run main.go serve grant" - volumes: - - .:/src - - ./test/secrets:/etc/kafka/secrets - working_dir: /src - depends_on: - - kafka - - postgres - - challenge-bypass - networks: - - grant - environment: - - PPROF_ENABLED=true - - ENABLE_LINKING_DRAINING=true - - DRAIN_RETRY_JOB_ENABLED=true - - ENV=local - - DEBUG=1 - - BAT_SETTLEMENT_ADDRESS - - BRAVE_TRANSFER_PROMOTION_ID - - CHALLENGE_BYPASS_SERVER=http://challenge-bypass:2416 - - CHALLENGE_BYPASS_TOKEN - - "DATABASE_MIGRATIONS_URL=file:///src/migrations" - - "DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable" - - ENCRYPTION_KEY=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0 - - DONOR_WALLET_CARD_ID - - DONOR_WALLET_PRIVATE_KEY - - DONOR_WALLET_PUBLIC_KEY - - GRANT_SIGNATOR_PUBLIC_KEY - - GRANT_WALLET_CARD_ID - - GRANT_WALLET_PRIVATE_KEY - - GRANT_WALLET_PUBLIC_KEY - - KAFKA_BROKERS=kafka:19092 - - KAFKA_SSL_CA_LOCATION=/etc/kafka/secrets/snakeoil-ca-1.crt - - KAFKA_SSL_CERTIFICATE_LOCATION=/etc/kafka/secrets/consumer-ca1-signed.pem - - KAFKA_SSL_KEY_LOCATION=/etc/kafka/secrets/consumer.client.key - - KAFKA_REQUIRED_ACKS=1 - - KAFKA_CONSUMER_GROUP_PROMOTIONS=grant-bat-promotions-local - - TOKEN_LIST - - UPHOLD_ACCESS_TOKEN - - "RATIOS_SERVICE=https://ratios.rewards.bravesoftware.com" - - RATIOS_TOKEN diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index d5b93339e..000000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,28 +0,0 @@ -version: "3.4" - -volumes: - out: - driver_opts: - type: tmpfs - device: tmpfs - -services: - dev: - volumes: - - ".:/src" - - "out:/out" - security_opt: - - no-new-privileges:true - environment: - - OUTPUT_DIR="/out" - - "CHALLENGE_BYPASS_DATABASE_URL=postgres://btokens:password@grant-postgres/btokens?sslmode=disable" - - "VAULT_ADDR=http://vault:8200" - - TEST_TAGS - - VAULT_TOKEN - - TEST_RUN - - TEST_PKG - vault: - container_name: grant-vault - image: vault:0.10.2 - networks: - - grant diff --git a/docker-compose.ext.yml b/docker-compose.ext.yml deleted file mode 100644 index da09ac48a..000000000 --- a/docker-compose.ext.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "3.4" - -networks: - brave_shared: - name: brave_shared_net - external: true - -services: - dev: - networks: - - grant - - brave_shared - - web: - networks: - - grant - - brave_shared diff --git a/docker-compose.reputation.yml b/docker-compose.reputation.yml deleted file mode 100644 index b8bed8a44..000000000 --- a/docker-compose.reputation.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: "3.4" - -networks: - grant: - driver: bridge - -services: - web: - environment: - - "REPUTATION_SERVER=http://reputation-web-grant:3334" - - "REPUTATION_TOKEN=foobarfoobar" - depends_on: - - reputation-web-grant - reputation-postgres-grant: - container_name: reputation-postgres-grant - image: postgres:14 - environment: - - "POSTGRES_USER=reputation" - - "POSTGRES_PASSWORD=password" - - "POSTGRES_DB=reputation" - networks: - - grant - ports: - - "3402:5432" - reputation-web-grant: - container_name: reputation-web-grant - image: reputation-web - # restart: always - ports: - - 3334:3334 - environment: - - "ENV=production" - - "ALLOW_DEBUG_APK=1" - - "API_APPLE_KEY_ID=1" - - "API_APPLE_BASE_URL=https://api.development.devicecheck.apple.com" - - "API_APPLE_PRIVATE_KEY=1" - - "API_APPLE_TEAM_ID=1" - - "DATABASE_URL=postgres://reputation:password@reputation-postgres-grant:5432/reputation?sslmode=disable" - - "DATABASE_MIGRATIONS_URL=file:///migrations" - - "GRPC_ENABLED=1" - - "IOS_ENROLLMENT_WAIT_HOURS=720h" - - "TOKEN_LIST=636765a9-1f92-4b40-ab0b-85ebd1e2c23d" - - "TEST_TAGS=integration" - - "GOTEST=go test" - depends_on: - - reputation-postgres-grant - networks: - - grant diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index ee66f10db..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,324 +0,0 @@ -version: "3.4" - -volumes: - pg_data: - out: - driver_opts: - type: tmpfs - device: tmpfs - -networks: - grant: - driver: bridge - -services: - dev: - container_name: grant-dev - build: - context: . - target: builder - args: - VERSION: "${VERSION}" - COMMIT: "${COMMIT}" - BUILD_TIME: "${BUILD_TIME}" - environment: - - "ALLOWED_ORIGINS=http://localhost:8080" - - AWS_ACCESS_KEY_ID=dummy - - AWS_SECRET_ACCESS_KEY=dummy - - AWS_REGION=us-west-2 - - BAT_SETTLEMENT_ADDRESS - - BITFLYER_CLIENT_ID - - BITFLYER_CLIENT_SECRET - - BITFLYER_EXTRA_CLIENT_SECRET - - BITFLYER_DRYRUN - - BITFLYER_SERVER - - BITFLYER_SOURCE_FROM - - BITFLYER_TOKEN - - BRAVE_TRANSFER_PROMOTION_ID - - CHALLENGE_BYPASS_SERVER=http://challenge-bypass:2416 - - CHALLENGE_BYPASS_TOKEN - - COINGECKO_APIKEY - - COINGECKO_SERVICE - - "DATABASE_MIGRATIONS_URL=file:///src/migrations" - - "DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable" - - DEBUG=1 - - DONOR_WALLET_CARD_ID - - DONOR_WALLET_PRIVATE_KEY - - DONOR_WALLET_PUBLIC_KEY - - DRAIN_RETRY_JOB_ENABLED=true - - "DYNAMODB_ENDPOINT=http://dynamodb:8000" - - ENABLE_LINKING_DRAINING=true - - ENV=local - - GEMINI_API_KEY - - GEMINI_API_SECRET - - GEMINI_BROWSER_CLIENT_ID - - GEMINI_CLIENT_ID - - GEMINI_CLIENT_SECRET - - GEMINI_SERVER - - GEMINI_TEST_DESTINATION_ID - - GRANT_CBP_SIGN_CONSUMER_TOPIC=sign.consumer # unsigned order creds request - - GRANT_CBP_SIGN_CONSUMER_TOPIC_DLQ=sign.consumer.dlq # unsigned order creds request dlq - - GRANT_CBP_SIGN_PRODUCER_TOPIC=sign.producer # signed orders creds result - - GRANT_SIGNATOR_PUBLIC_KEY - - GRANT_WALLET_CARD_ID - - GRANT_WALLET_PRIVATE_KEY - - GRANT_WALLET_PUBLIC_KEY - - KAFKA_BROKERS=kafka:19092 - - KAFKA_CONSUMER_GROUP_PROMOTIONS=grant-bat-promotions-local - - KAFKA_CONSUMER_GROUP_SIGNED_ORDER_CREDENTIALS=grant-bat-skus-local - - KAFKA_REQUIRED_ACKS=1 - - KAFKA_SSL_CA_LOCATION=/etc/kafka/secrets/snakeoil-ca-1.crt - - KAFKA_SSL_CERTIFICATE_LOCATION=/etc/kafka/secrets/consumer-ca1-signed.pem - - KAFKA_SSL_KEY_LOCATION=/etc/kafka/secrets/consumer.client.key - - PPROF_ENABLED=true - - "RATIOS_SERVICE=https://ratios.rewards.bravesoftware.com" - - RATIOS_TOKEN - - REPUTATION_SERVER - - REPUTATION_TOKEN - - SKUS_WHITELIST - - TEST_PKG - - TEST_RUN - - TOKEN_LIST - - UPHOLD_ACCESS_TOKEN - volumes: - - ./test/secrets:/etc/kafka/secrets - - ./migrations:/src/migrations - - "out:/out" - depends_on: - redis: - condition: service_healthy - postgres: - condition: service_healthy - challenge-bypass: - condition: service_started - kafka: - condition: service_healthy - networks: - - grant - - web: - container_name: grant-web - build: - context: . - target: artifact - args: - VERSION: "${VERSION}" - COMMIT: "${COMMIT}" - BUILD_TIME: "${BUILD_TIME}" - ports: - - "3333:3333" - - "6061:6061" - security_opt: - - no-new-privileges:true - environment: - - AWS_ACCESS_KEY_ID=dummy - - AWS_SECRET_ACCESS_KEY=dummy - - AWS_REGION=us-west-2 - - BAT_SETTLEMENT_ADDRESS - - BITFLYER_CLIENT_ID - - BITFLYER_CLIENT_SECRET - - BITFLYER_EXTRA_CLIENT_SECRET - - BITFLYER_DRYRUN - - BITFLYER_SERVER - - BITFLYER_SOURCE_FROM - - BITFLYER_TOKEN - - CHALLENGE_BYPASS_SERVER=http://challenge-bypass:2416 - - CHALLENGE_BYPASS_TOKEN - - COINGECKO_APIKEY - - COINGECKO_SERVICE - - "DATABASE_MIGRATIONS_URL=file:///src/migrations" - - "DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable" - - DEBUG=1 - - DONOR_WALLET_CARD_ID - - DONOR_WALLET_PRIVATE_KEY - - DONOR_WALLET_PUBLIC_KEY - - DRAIN_RETRY_JOB_ENABLED=true - - "DYNAMODB_ENDPOINT=http://dynamodb:8000" - - ENABLE_LINKING_DRAINING=true - - ENCRYPTION_KEY - - ENV=local - - FEATURE_MERCHANT - - GEMINI_API_KEY - - GEMINI_API_SECRET - - ZEBPAY_API_KEY - - ZEBPAY_SIGNING_KEY - - ZEBPAY_SERVER - - ZEBPAY_TEST_DESTINATION - - ZEBPAY_TEST_FROM - - GEMINI_BROWSER_CLIENT_ID - - GEMINI_CLIENT_ID - - GEMINI_CLIENT_SECRET - - GEMINI_SERVER - - GEMINI_TEST_DESTINATION_ID - - GRANT_CBP_SIGN_CONSUMER_TOPIC=sign.consumer # unsigned order creds request - - GRANT_CBP_SIGN_CONSUMER_TOPIC_DLQ=sign.consumer.dlq # unsigned order creds request dlq - - GRANT_CBP_SIGN_PRODUCER_TOPIC=sign.producer # signed orders creds result - - GRANT_SIGNATOR_PUBLIC_KEY - - GRANT_WALLET_CARD_ID - - GRANT_WALLET_PRIVATE_KEY - - GRANT_WALLET_PUBLIC_KEY - - KAFKA_BROKERS=kafka:19092 - - KAFKA_CONSUMER_GROUP_PROMOTIONS=grant-bat-promotions-local - - KAFKA_CONSUMER_GROUP_SIGNED_ORDER_CREDENTIALS=grant-bat-skus-local - - KAFKA_REQUIRED_ACKS=1 - - KAFKA_SSL_CA_LOCATION=/etc/kafka/secrets/snakeoil-ca-1.crt - - KAFKA_SSL_CERTIFICATE_LOCATION=/etc/kafka/secrets/consumer-ca1-signed.pem - - KAFKA_SSL_KEY_LOCATION=/etc/kafka/secrets/consumer.client.key - - OUTPUT_DIR="/out" - - PKG - - PPROF_ENABLED=true - - RUN - - TEST_PKG - - TEST_RUN - - TOKEN_LIST - - UPHOLD_ACCESS_TOKEN - volumes: - - ./test/secrets:/etc/kafka/secrets - - ./migrations:/src/migrations - depends_on: - redis: - condition: service_healthy - postgres: - condition: service_healthy - challenge-bypass: - condition: service_started - kafka: - condition: service_healthy - networks: - - grant - - redis: - container_name: grant-redis - image: redis - ports: - - "6379:6379" - networks: - - grant - healthcheck: - test: ["CMD-SHELL", "redis-cli ping | grep PONG"] - interval: 5s - timeout: 5s - retries: 5 - start_period: 5s - - postgres: - container_name: grant-postgres - image: postgres:14 - ports: - - "3401:5432" - environment: - - "POSTGRES_USER=grants" - - "POSTGRES_PASSWORD=password" - - "POSTGRES_EXTRA_DATABASES=btokens:btokens:password" - - "TZ=UTC" - networks: - - grant - command: ["postgres", "-c", "log_statement=all"] - volumes: - - pg_data:/var/lib/postgresql/data - - ./create_dbs.sh:/docker-entrypoint-initdb.d/00_create_dbs.sh - healthcheck: - test: ["CMD-SHELL", "pg_isready -U grants -d grants"] - interval: 5s - timeout: 5s - retries: 5 - start_period: 5s - - challenge-bypass: - container_name: challenge-bypass - image: brave/challenge-bypass:latest - restart: always - ports: - - "2416:2416" - environment: - - "ENV=devtest" - - "SENTRY_DSN" - - "DATABASE_URL=postgres://btokens:password@grant-postgres/btokens?sslmode=disable" - - "DATABASE_MIGRATIONS_URL=file:///src/migrations" - - KAFKA_BROKERS=kafka:19092 - - KAFKA_SSL_CA_LOCATION=/etc/kafka/secrets/snakeoil-ca-1.crt - - KAFKA_SSL_CERTIFICATE_LOCATION=/etc/kafka/secrets/consumer-ca1-signed.pem - - KAFKA_SSL_KEY_LOCATION=/etc/kafka/secrets/consumer.client.key - - KAFKA_REQUIRED_ACKS=1 - - KAFKA_CONSUMERS_PER_NODE=1 - - REDEEM_CONSUMER_TOPIC=redeem.consumer - - REDEEM_PRODUCER_TOPIC=redeem.producer - - SIGN_CONSUMER_TOPIC=sign.consumer # unsigned order creds request - - SIGN_PRODUCER_TOPIC=sign.producer # signed orders creds result - - CONSUMER_GROUP=consumer.group - - "DYNAMODB_ENDPOINT=http://dynamodb:8000" - - AWS_ACCESS_KEY_ID=dummy - - AWS_SECRET_ACCESS_KEY=dummy - - AWS_REGION=us-west-2 - depends_on: - postgres: - condition: service_healthy - dynamodb: - condition: service_healthy - volumes: - - ./test/secrets:/etc/kafka/secrets - networks: - - grant - - zookeeper: - container_name: grant-zookeeper - image: confluentinc/cp-zookeeper:7.3.1 - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - ports: - - "2181:2181" - networks: - - grant - healthcheck: - test: ["CMD-SHELL", "nc -z localhost 2181 || exit 1"] - interval: 10s - timeout: 5s - retries: 10 - start_period: 10s - - kafka: - container_name: grant-kafka - image: confluentinc/cp-kafka:7.3.1 - ports: - - "29092:29092" - environment: - KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" - KAFKA_ADVERTISED_LISTENERS: "SSL://kafka:19092,SSL2://localhost:29092" - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: SSL:SSL,SSL2:SSL - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - KAFKA_SSL_KEYSTORE_FILENAME: kafka.broker1.keystore.jks - KAFKA_SSL_KEYSTORE_CREDENTIALS: broker1_keystore_creds - KAFKA_SSL_KEY_CREDENTIALS: broker1_sslkey_creds - KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.broker1.truststore.jks - KAFKA_SSL_TRUSTSTORE_CREDENTIALS: broker1_truststore_creds - KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: " " - KAFKA_SSL_CLIENT_AUTH: requested - KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL - KAFKA_RETENTION_MS: 10000 - volumes: - - ./test/secrets:/etc/kafka/secrets - depends_on: - zookeeper: - condition: service_healthy - networks: - - grant - healthcheck: - test: ["CMD-SHELL", "nc -z localhost 29092 || exit 1"] - interval: 10s - timeout: 5s - retries: 10 - start_period: 10s - - dynamodb: - container_name: dynamodb - image: amazon/dynamodb-local:1.21.0 - networks: - - grant - ports: - - "8000:8000" - healthcheck: - test: ["CMD-SHELL", "curl -Is http://localhost:8000/shell/ | grep HTTP || exit 1"] - interval: 5s - timeout: 5s - retries: 5 - start_period: 5s diff --git a/ecs-vars-dev.env b/ecs-vars-dev.env deleted file mode 100644 index 331071f80..000000000 --- a/ecs-vars-dev.env +++ /dev/null @@ -1,24 +0,0 @@ -# Non-Sensitive Task Environment Variables for Grant Development - -ANON_CARD_SETTLEMENT_ADDRESS=9094c3f2-b3ae-438f-bd59-92aaad92de5c -BALANCE_SERVER=https://balance.rewards.brave.software -BAT_SETTLEMENT_ADDRESS=9094c3f2-b3ae-438f-bd59-92aaad92de5c -BRAVE_TRANSFER_PROMOTION_IDS=23364b75-0bc1-4e28-a96d-5238c9a9e35b f7faf1f5-ec3e-4027-a38b-cbc9d2400c52 7fe5afb4-dae4-4a7d-9f3f-0784df293608 526ce6b5-97bb-4a3e-9a6a-181e951345ef -DATABASE_MIGRATIONS_URL=file://migrations -DEBUG=0 -ENABLE_JOB_WORKERS=true -ENABLE_LINKING_DRAINING=true -GRANT_SIGNATOR_PUBLIC_KEY=90f006eecd80f21bd26dc363155355c2b857cdd9d77b550db23bccaa2866c3c3 -GRANT_WALLET_CARD_ID=9094c3f2-b3ae-438f-bd59-92aaad92de5c -GRANT_WALLET_PUBLIC_KEY=37bb424aa56661058768d6d074163162d50c8f6f9ed64f2f491aac307f0acade -KAFKA_BROKERS=b-3.bsg-sandbox-msk-cluste.ia2btu.c3.kafka.us-west-2.amazonaws.com:9094,b-1.bsg-sandbox-msk-cluste.ia2btu.c3.kafka.us-west-2.amazonaws.com:9094,b-2.bsg-sandbox-msk-cluste.ia2btu.c3.kafka.us-west-2.amazonaws.com:9094 -KAFKA_CONSUMER_GROUP=bat-eyeshade -KAFKA_CONSUMER_GROUP_PROMOTIONS=grant-bat-promotions-dev -KAFKA_REQUIRED_ACKS=2 -PPROF_ENABLED=true -UPHOLD_ENVIRONMENT=sandbox -UPHOLD_SETTLEMENT_ADDRESS=9094c3f2-b3ae-438f-bd59-92aaad92de5c -UPHOLD_WALLET_LINKING_LIMIT=1000 -WALLET_ON_PLATFORM_PRIOR_TO=2020-12-20T00:00:00Z -WALLETS_IN_MIGRATION=false - diff --git a/tools/payments/cmd/disaster/go.mod b/go.mod similarity index 51% rename from tools/payments/cmd/disaster/go.mod rename to go.mod index 573d6caf4..30ab440da 100644 --- a/tools/payments/cmd/disaster/go.mod +++ b/go.mod @@ -1,29 +1,49 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/disaster - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs +module github.com/brave-intl/payments-service go 1.21 toolchain go1.22.0 +replace github.com/brave-intl/bat-go/libs => github.com/brave-intl/bat-go/libs v0.0.0-20240724150637-6cf2deb377b3 + +require github.com/brave-intl/bat-go/libs v0.0.0-20240724150637-6cf2deb377b3 + require ( filippo.io/age v1.1.1 + github.com/amazon-ion/ion-go v1.2.0 + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/aws/aws-sdk-go v1.50.13 + github.com/aws/aws-sdk-go-v2 v1.26.1 github.com/aws/aws-sdk-go-v2/config v1.27.11 + github.com/aws/aws-sdk-go-v2/credentials v1.17.11 + github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 + github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6 + github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10 github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 - github.com/brave-intl/bat-go/tools/payments v0.0.0-20240509075650-57962c96e6db + github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 + github.com/aws/smithy-go v1.20.2 + github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1 + github.com/blocto/solana-go-sdk v1.27.0 + github.com/getsentry/sentry-go v0.14.0 + github.com/go-chi/chi v4.1.2+incompatible + github.com/google/uuid v1.6.0 github.com/hashicorp/vault v1.16.2 + github.com/jarcoal/httpmock v1.3.0 + github.com/mdlayher/vsock v1.2.0 + github.com/mr-tron/base58 v1.2.0 + github.com/rs/zerolog v1.28.0 + github.com/shopspring/decimal v1.3.1 + github.com/spf13/cobra v1.6.1 + github.com/spf13/viper v1.13.0 + github.com/stretchr/testify v1.9.0 + golang.org/x/crypto v0.22.0 ) require ( filippo.io/edwards25519 v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect + github.com/amzn/ion-go v1.1.3 // indirect + github.com/amzn/ion-hash-go v1.1.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect @@ -35,42 +55,53 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect - github.com/aws/smithy-go v1.20.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fxamacker/cbor/v2 v2.2.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/redis/go-redis/v9 v9.3.0 // indirect github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.4.1 // indirect github.com/throttled/throttled v2.2.5+incompatible // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.22.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cmd/go.sum b/go.sum similarity index 76% rename from cmd/go.sum rename to go.sum index 38a8170be..5fe5870af 100644 --- a/cmd/go.sum +++ b/go.sum @@ -36,6 +36,10 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= +filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -44,13 +48,96 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/alicebob/miniredis/v2 v2.23.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= +github.com/amazon-ion/ion-go v1.2.0 h1:EgFy23/7gRxRYdUkJARh/7eZc8BYkFFDZZSqB3PwVqQ= +github.com/amazon-ion/ion-go v1.2.0/go.mod h1:3ZEje8i20TiIPVZlN+KE3B2ppZ1B8d9F/KaT7Dtec+k= +github.com/amzn/ion-go v1.1.3 h1:gGhjtLY0GUNQXej5N2qHhoVWQBkgtoPDt1feYYFMfOc= +github.com/amzn/ion-go v1.1.3/go.mod h1:7wQBWQ7PhPpZCr9PL+mtuIyNmyLjuV8qt2mrfxmvkA8= +github.com/amzn/ion-hash-go v1.1.2 h1:cUEolXoS7aPwYFknwae47zppF+gJgZEWqRiRbPdPIy8= +github.com/amzn/ion-hash-go v1.1.2/go.mod h1:6DKfguDnpHlHE8fHV7CxZiWnEudDxMDXUkSn2fu3j/4= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.50.13 h1:yeXram2g7q8uKkQkAEeZyk9FmPzxI4UpGwAZGZtEGmM= +github.com/aws/aws-sdk-go v1.50.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= +github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= +github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg= +github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= +github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA= +github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE= +github.com/aws/aws-sdk-go-v2/credentials v1.12.23/go.mod h1:0awX9iRr/+UO7OwRQFpV1hNtXxOVuehpjVEzrIAYNcA= +github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs= +github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 h1:81KE7vaZzrl7yHBYHVEzYB8sypz11NMOZ40YlWvPxsU= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5/go.mod h1:LIt2rg7Mcgn09Ygbdh/RdIm0rQ+3BNkbP1gyVMFtRK0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 h1:ZMeFZ5yk+Ek+jNr1+uwCd2tG89t6oTS5yVWpa6yy2es= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7/go.mod h1:mxV05U+4JiHqIpGqqYXOHLPKUC6bDXC44bsUhNjOEwY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 h1:f9RyWNtS8oH7cZlbn+/JNPpjUk5+5fLd5lM9M0i49Ys= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5/go.mod h1:h5CoMZV2VF297/VLhRhO1WF+XYWOzXo+4HsObA4HjBQ= +github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU= +github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4= +github.com/aws/aws-sdk-go-v2/service/qldb v1.14.20/go.mod h1:9morR/lAo8ziBkYz5gxtGe0FzAjkcUfxAqSk0Q9obDc= +github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6 h1:BFK9rvyhv4OyxrZa0w4ItE2s5L2Zg/hMu9jA286YWvg= +github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6/go.mod h1:+hUHf6G2dhZJcVUVNqqCmjezYNZu07akDOtc72upBEQ= +github.com/aws/aws-sdk-go-v2/service/qldbsession v1.13.19/go.mod h1:xX3iRpzN9iJYgP45OUYHJWFLrre0/s1Mur8SjOwg3RU= +github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10 h1:xZuYRwfZ1nFBWQVjXaegmvZcqsfsbzDYyXxXXdujKN4= +github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10/go.mod h1:U0ZDG2WjWfnUWl7PiQo2+YphzlMteFj2RhjoBgyns0E= +github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 h1:6cnno47Me9bRykw9AEv9zkXE+5or7jz8TsskTTccbgc= +github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1/go.mod h1:qmdkIIAC+GCLASF7R2whgNrJADz0QZPX+Seiw/i4S3o= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.1/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= +github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1 h1:kl2z0sTngGlrfGqDDwOek573S2AJ6Ys+Wrf8I0b0B6A= +github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1/go.mod h1:VapwwZVNh07sUP9oTiH4Td+g5E6dCoR2bcnbTuwakJw= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blocto/solana-go-sdk v1.27.0 h1:nIsV0S0Hu7M0SktkgdDuTI/mM4FLyoInpu5M7wsl2W4= +github.com/blocto/solana-go-sdk v1.27.0/go.mod h1:Xoyhhb3hrGpEQ5rJps5a3OgMwDpmEhrd9bgzFKkkwMs= +github.com/brave-intl/bat-go/libs v0.0.0-20240724150637-6cf2deb377b3 h1:IjaH0D0ykhEzVypUtml/zwv86tukfSu4FnSRxXHtx7M= +github.com/brave-intl/bat-go/libs v0.0.0-20240724150637-6cf2deb377b3/go.mod h1:zj+4u3tkUgacysxwEPM/g6Mrv0vIBRKFHmlY0b4KlBw= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -65,16 +152,21 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ= @@ -83,10 +175,13 @@ github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWS github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -110,6 +205,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -125,8 +221,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -141,7 +238,10 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -158,15 +258,19 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= +github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= +github.com/hashicorp/vault v1.16.2 h1:8usz1tMhWcl18PbdjQPKoL5UmenA2n4ZKB3oM04jTck= +github.com/hashicorp/vault v1.16.2/go.mod h1:QqhZjf0dS0krRty6Wf8iSMx2EnIjAJSCpZ57jEmkQec= github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= @@ -175,6 +279,11 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= +github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -190,21 +299,27 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= +github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= @@ -216,33 +331,39 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 h1:lFN7TVecCMbCHVNfEofDqqaVsuAlkFyDmmO7EF4nXj4= +github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454/go.mod h1:NeMochZp7jN/pYFuxLkrZtmLqbADmnp/y1+/dL+AsyQ= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -256,8 +377,11 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= +github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= @@ -268,13 +392,14 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -286,14 +411,20 @@ github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+z github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= @@ -304,7 +435,9 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= +github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -317,10 +450,14 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -331,8 +468,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -356,6 +491,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -390,10 +527,14 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -415,8 +556,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -448,6 +592,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -465,11 +610,22 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -478,8 +634,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -531,6 +689,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -625,13 +785,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -639,10 +800,12 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/libs/altcurrency/altcurrency.go b/libs/altcurrency/altcurrency.go deleted file mode 100644 index 8a5874e54..000000000 --- a/libs/altcurrency/altcurrency.go +++ /dev/null @@ -1,161 +0,0 @@ -// Package altcurrency provides an enum-like representation of acceptable cryptocurrencies as -// well as helper functions for tasks like validating addresses and converting currency units. -package altcurrency - -import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "errors" - "strconv" - "strings" - "unicode" - - "github.com/btcsuite/btcutil/base58" - "github.com/shopspring/decimal" - "golang.org/x/crypto/sha3" -) - -// AltCurrency is an enum-like representing a cryptocurrency -// FIXME change this to a struct instead of a type alias? -type AltCurrency int - -const ( - invalid AltCurrency = iota - // BAT Basic Attention Token - BAT - // BTC Bitcoin - BTC - // ETH Ethereum - ETH - // LTC Litecoin - LTC -) - -var altCurrencyName = map[AltCurrency]string{ - BAT: "BAT", - BTC: "BTC", - ETH: "ETH", - LTC: "LTC", -} - -var altCurrencyID = map[string]AltCurrency{ - "BAT": BAT, - "BTC": BTC, - "ETH": ETH, - "LTC": LTC, -} - -var altCurrencyDecimals = map[AltCurrency]int32{ - BAT: 18, - BTC: 8, - ETH: 18, - LTC: 8, -} - -// IsValid returns true if a is a valid AltCurrency. -func (a AltCurrency) IsValid() bool { - _, exists := altCurrencyName[a] - if !exists || a == invalid { - return false - } - return true -} - -// Scale returns the scalar used to convert between the subunit and the base unit. -// For example in bitcoin this will be 10^8, as there are 10^8 satoshis (subunit) -// in one bitcoin (base unit). -// https://en.wikipedia.org/wiki/Denomination_(currency)#Subunit_and_super_unit -func (a AltCurrency) Scale() decimal.Decimal { - return decimal.New(1, altCurrencyDecimals[a]) -} - -// ToProbi converts v, denominated in base units to sub units of AltCurrency a. -func (a AltCurrency) ToProbi(v decimal.Decimal) decimal.Decimal { - return v.Mul(a.Scale()) -} - -// FromProbi converts v, denominated in subunits to base units of AltCurrency a. -func (a AltCurrency) FromProbi(v decimal.Decimal) decimal.Decimal { - return v.DivRound(a.Scale(), altCurrencyDecimals[a]) -} - -func (a AltCurrency) String() string { - return altCurrencyName[a] -} - -// MarshalText marshalls the altcurrency into text. -func (a *AltCurrency) MarshalText() (text []byte, err error) { - if *a == invalid { - return nil, errors.New("not a valid AltCurrency") - } - text = []byte(a.String()) - return -} - -// UnmarshalText unmarshalls the altcurrency from text. -func (a *AltCurrency) UnmarshalText(text []byte) (err error) { - *a, err = FromString(string(text)) - return err -} - -// FromString returns the corresponding AltCurrency or error if there is none -func FromString(text string) (AltCurrency, error) { - a, exists := altCurrencyID[text] - if !exists { - return invalid, errors.New("not a valid AltCurrency") - } - return a, nil -} - -// GetBTCAddressVersion returns the BTC address version of the address str. -func GetBTCAddressVersion(str string) int { - addr := base58.Decode(str) - if len(addr) != 25 { - return -1 - } - version := addr[0] - checksum := addr[len(addr)-4:] - vh160 := addr[:len(addr)-4] - - sum := sha256.Sum256(vh160) - sum = sha256.Sum256(sum[:]) - if !bytes.Equal(sum[0:4], checksum) { - return -1 - } - - return int(version) -} - -// Keccak256 calculates and returns the Keccak256 hash of the input data. -// Copied from https://github.com/ethereum/go-ethereum/, licensed under the GNU General Public License v3.0 -func Keccak256(data ...[]byte) []byte { - d := sha3.NewLegacyKeccak256() - for _, b := range data { - _, err := d.Write(b) - if err != nil { - panic(err) - } - } - return d.Sum(nil) -} - -// ToChecksumETHAddress returns the address str with a checksum encoded in the capitalization per EIP55 -func ToChecksumETHAddress(str string) string { - lower := strings.Replace(strings.ToLower(str), "0x", "", 1) - lowerBytes := []byte(lower) - hash := Keccak256([]byte(lower)) - hashHex := make([]byte, hex.EncodedLen(len(hash))) - hex.Encode(hashHex, hash) - - for i, v := range lowerBytes { - x, err := strconv.ParseUint(string([]byte{hashHex[i]}), 16, 8) - if err != nil { - panic(err) - } - if x >= 8 { - lowerBytes[i] = byte(unicode.ToUpper(rune(v))) - } - } - return "0x" + string(lowerBytes) -} diff --git a/libs/altcurrency/altcurrency_test.go b/libs/altcurrency/altcurrency_test.go deleted file mode 100644 index 32dad67a6..000000000 --- a/libs/altcurrency/altcurrency_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package altcurrency - -import ( - "encoding/json" - "testing" - - "github.com/shopspring/decimal" -) - -func TestJsonUnmarshal(t *testing.T) { - var a AltCurrency - err := json.Unmarshal([]byte("\"BAT\""), &a) - if err != nil { - t.Error("Unexpected error during unmarshal") - } - if a != BAT { - t.Error("Unexpected altcurrency to be BAT") - t.Error(a) - } - - err = json.Unmarshal([]byte("\"FOO\""), &a) - if err == nil { - t.Error("Expected error during unmarshal") - } - - err = json.Unmarshal([]byte("\"INVALID\""), &a) - if err == nil { - t.Error("Expected error during unmarshal") - } -} - -func TestJsonMarshal(t *testing.T) { - var a AltCurrency - _, err := json.Marshal(&a) - if err == nil { - t.Error("Expected error during marshal of uninitialized altcurrency") - } - a = ETH - b, err := json.Marshal(&a) - if err != nil { - t.Error("Unexpected error during marshal") - } - if string(b) != "\"ETH\"" { - t.Error("Incorrect string value from marshal") - } -} - -func TestFromProbi(t *testing.T) { - i, err := decimal.NewFromString("123456789") - if err != nil { - t.Error(err) - } - btc := BTC.FromProbi(i) - expectedBtc, err := decimal.NewFromString("1.23456789") - if err != nil { - t.Error(err) - } - if !btc.Equals(expectedBtc) { - t.Error("Expected satoshi value to match BTC value") - } - - i, err = decimal.NewFromString("1234567898765432123") - if err != nil { - t.Error(err) - } - - eth := ETH.FromProbi(i) - expectedEth, err := decimal.NewFromString("1.234567898765432123") - if err != nil { - t.Error(err) - } - - if !eth.Equals(expectedEth) { - t.Error(eth) - t.Error(expectedEth) - t.Error("Expected wei value to match ETH value") - } -} - -func TestToProbi(t *testing.T) { - f, err := decimal.NewFromString("1.23456789") - if err != nil { - t.Error(err) - } - satoshi := BTC.ToProbi(f) - expectedSatoshi, err := decimal.NewFromString("123456789") - if err != nil { - t.Error(err) - } - - if !satoshi.Equals(expectedSatoshi) { - t.Error("Expected satoshi value to match BTC value") - } - - f, err = decimal.NewFromString("1.234567898765432123") - if err != nil { - t.Error(err) - } - - wei := ETH.ToProbi(f) - expectedWei, err := decimal.NewFromString("1234567898765432123") - if err != nil { - t.Error(err) - } - - if !wei.Equals(expectedWei) { - t.Error("Expected wei value to match ETH value") - } -} - -func TestToChecksumETHAddress(t *testing.T) { - addr := ToChecksumETHAddress("0xf1a61415e12db93abace8704855a4795934ff992") - if addr != "0xF1A61415e12DB93ABACE8704855A4795934ff992" { - t.Error("Unexpected adding checksum to ETH address") - } -} diff --git a/libs/aws/kms.go b/libs/aws/kms.go deleted file mode 100644 index e7a4fd64e..000000000 --- a/libs/aws/kms.go +++ /dev/null @@ -1,30 +0,0 @@ -package aws - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -// KMSCreateKeyAPI defines the interface for the CreateKey function. -// We use this interface to test the function using a mocked service. -type KMSCreateKeyAPI interface { - CreateKey(ctx context.Context, - params *kms.CreateKeyInput, - optFns ...func(*kms.Options)) (*kms.CreateKeyOutput, error) -} - -// MakeKey creates an AWS Key Management Service (AWS KMS) key (KMS key). -// Inputs: -// -// c is the context of the method call, which includes the AWS Region. -// api is the interface that defines the method call. -// input defines the input arguments to the service call. -// -// Output: -// -// If success, a CreateKeyOutput object containing the result of the service call and nil. -// Otherwise, nil and an error from the call to CreateKey. -func MakeKey(c context.Context, api KMSCreateKeyAPI, input *kms.CreateKeyInput) (*kms.CreateKeyOutput, error) { - return api.CreateKey(c, input) -} diff --git a/libs/aws/mock/mock.go b/libs/aws/mock/mock.go deleted file mode 100644 index 8b0ae21cf..000000000 --- a/libs/aws/mock/mock.go +++ /dev/null @@ -1,56 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./aws/s3.go - -// Package mockaws is a generated GoMock package. -package mockaws - -import ( - context "context" - reflect "reflect" - - s3 "github.com/aws/aws-sdk-go-v2/service/s3" - gomock "github.com/golang/mock/gomock" -) - -// MockS3GetObjectAPI is a mock of S3GetObjectAPI interface. -type MockS3GetObjectAPI struct { - ctrl *gomock.Controller - recorder *MockS3GetObjectAPIMockRecorder -} - -// MockS3GetObjectAPIMockRecorder is the mock recorder for MockS3GetObjectAPI. -type MockS3GetObjectAPIMockRecorder struct { - mock *MockS3GetObjectAPI -} - -// NewMockS3GetObjectAPI creates a new mock instance. -func NewMockS3GetObjectAPI(ctrl *gomock.Controller) *MockS3GetObjectAPI { - mock := &MockS3GetObjectAPI{ctrl: ctrl} - mock.recorder = &MockS3GetObjectAPIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockS3GetObjectAPI) EXPECT() *MockS3GetObjectAPIMockRecorder { - return m.recorder -} - -// GetObject mocks base method. -func (m *MockS3GetObjectAPI) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, params} - for _, a := range optFns { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GetObject", varargs...) - ret0, _ := ret[0].(*s3.GetObjectOutput) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetObject indicates an expected call of GetObject. -func (mr *MockS3GetObjectAPIMockRecorder) GetObject(ctx, params interface{}, optFns ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, params}, optFns...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObject", reflect.TypeOf((*MockS3GetObjectAPI)(nil).GetObject), varargs...) -} diff --git a/libs/aws/s3.go b/libs/aws/s3.go deleted file mode 100644 index 996448a7f..000000000 --- a/libs/aws/s3.go +++ /dev/null @@ -1,54 +0,0 @@ -package aws - -import ( - "context" - "fmt" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/s3" - awslogging "github.com/aws/smithy-go/logging" - appctx "github.com/brave-intl/bat-go/libs/context" - - "github.com/rs/zerolog" -) - -// S3GetObjectAPI - interface to allow for a GetObject mock -type S3GetObjectAPI interface { - GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) -} - -// Client defines the aws client. -type Client struct { - S3GetObjectAPI -} - -// NewClient creates a new aws client instance. -func NewClient(cfg aws.Config) (*Client, error) { - return &Client{ - S3GetObjectAPI: s3.NewFromConfig(cfg), - }, nil -} - -// BaseAWSConfig return an aws.Config with region and logger. -// Default region is us-west-2. -func BaseAWSConfig(ctx context.Context, logger *zerolog.Logger) (aws.Config, error) { - region, ok := ctx.Value(appctx.AWSRegionCTXKey).(string) - if !ok || len(region) == 0 { - region = "us-west-2" - } - // aws config - return config.LoadDefaultConfig( - ctx, - config.WithLogger(&appLogger{logger}), - config.WithRegion(region)) -} - -type appLogger struct { - *zerolog.Logger -} - -// Logf - implement smithy-go/logging.Logger -func (al *appLogger) Logf(classification awslogging.Classification, format string, v ...interface{}) { - al.Debug().Msg(fmt.Sprintf(format, v...)) -} diff --git a/libs/backoff/backoff.go b/libs/backoff/backoff.go deleted file mode 100644 index 2e2a8fbd8..000000000 --- a/libs/backoff/backoff.go +++ /dev/null @@ -1,49 +0,0 @@ -package backoff - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/backoff/retrypolicy" -) - -type ( - // RetryFunc defines a retry function - RetryFunc func(ctx context.Context, operation Operation, retryPolicy retrypolicy.Retry, IsRetriable IsRetriable) (interface{}, error) - - // Operation the operation to be executed with retry - Operation func() (interface{}, error) - - // IsRetriable a function to determine if an error caused by the executed operation is retriable - IsRetriable func(error) bool -) - -// Retry executes the given Operation using the provided retrypolicy.Retry policy and IsRetriable conditions -func Retry(ctx context.Context, operation Operation, retryPolicy retrypolicy.Retry, IsRetriable IsRetriable) (interface{}, error) { - - var err error - var response interface{} - var next time.Duration - - for { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - - if response, err = operation(); err == nil { - return response, nil - } - - if !IsRetriable(err) { - return nil, err - } - - if next = retryPolicy.CalculateNextDelay(); next == retrypolicy.Done { - return nil, err - } - - time.Sleep(next) - } - } -} diff --git a/libs/backoff/backoff_test.go b/libs/backoff/backoff_test.go deleted file mode 100644 index 234eea25c..000000000 --- a/libs/backoff/backoff_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package backoff - -import ( - "context" - "errors" - "testing" - "time" - - mockretrypolicy "github.com/brave-intl/bat-go/libs/backoff/retrypolicy/mock" - - "github.com/brave-intl/bat-go/libs/backoff/retrypolicy" - testutils "github.com/brave-intl/bat-go/libs/test" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -func TestRetry_CxtDone(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - ctx, done := context.WithCancel(context.Background()) - - operation := func() (interface{}, error) { - assert.Fail(t, "should not have been executed") - return nil, nil - } - - policy := mockretrypolicy.NewMockRetry(mockCtrl) - - isRetriable := func(error) bool { - assert.Fail(t, "should not have been executed") - return false - } - - done() - response, err := Retry(ctx, operation, policy, isRetriable) - - assert.Nil(t, response) - assert.ErrorIs(t, err, context.Canceled) -} - -func TestRetry_IsRetriable_False(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - ctx, done := context.WithCancel(context.Background()) - defer done() - - expected := errors.New(testutils.RandomString()) - - operation := func() (interface{}, error) { - return nil, expected - } - - policy := mockretrypolicy.NewMockRetry(mockCtrl) - - isRetriable := func(error) bool { - return false - } - - response, err := Retry(ctx, operation, policy, isRetriable) - - assert.Nil(t, response) - assert.ErrorIs(t, err, expected) -} - -func TestRetry_CalculateNextDelay_Done(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - ctx, done := context.WithCancel(context.Background()) - defer done() - - expected := errors.New(testutils.RandomString()) - - operation := func() (interface{}, error) { - return nil, expected - } - - policy := mockretrypolicy.NewMockRetry(mockCtrl) - policy.EXPECT(). - CalculateNextDelay(). - Return(retrypolicy.Done) - - isRetriable := func(error) bool { - return true - } - - response, err := Retry(ctx, operation, policy, isRetriable) - - assert.Nil(t, response) - assert.ErrorIs(t, err, expected) -} - -func TestRetry(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - ctx, done := context.WithCancel(context.Background()) - defer done() - - count := 0 - attempts := 2 - - operation := func() (interface{}, error) { - if count < attempts { - count++ - return nil, errors.New(testutils.RandomString()) - } - // return on third attempt - return "success", nil - } - - policy := mockretrypolicy.NewMockRetry(mockCtrl) - policy.EXPECT(). - CalculateNextDelay(). - Return(time.Second * 0). - Times(attempts) - - isRetriable := func(error) bool { - return true - } - - response, err := Retry(ctx, operation, policy, isRetriable) - - assert.Nil(t, err) - assert.NotNil(t, response) -} diff --git a/libs/backoff/retrypolicy/factory.go b/libs/backoff/retrypolicy/factory.go deleted file mode 100644 index 761e5454b..000000000 --- a/libs/backoff/retrypolicy/factory.go +++ /dev/null @@ -1,25 +0,0 @@ -package retrypolicy - -// Convenience retry policies - -import "time" - -var ( - // DefaultRetry a default policy - DefaultRetry, _ = New( - WithInitialInterval(50*time.Millisecond), - WithBackoffCoefficient(2.0), - WithMaximumInterval(10*time.Second), - WithExpirationInterval(time.Minute), - WithMaximumAttempts(10), - ) - - // NoRetry policy to be used if no retries are required - NoRetry, _ = New( - WithInitialInterval(0), - WithBackoffCoefficient(0), - WithMaximumInterval(0), - WithExpirationInterval(0), - WithMaximumAttempts(0), - ) -) diff --git a/libs/backoff/retrypolicy/mock/retrypolicy.go b/libs/backoff/retrypolicy/mock/retrypolicy.go deleted file mode 100644 index b011060fb..000000000 --- a/libs/backoff/retrypolicy/mock/retrypolicy.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./backoff/retrypolicy/retrypolicy.go - -// Package mockretrypolicy is a generated GoMock package. -package mockretrypolicy - -import ( - reflect "reflect" - time "time" - - gomock "github.com/golang/mock/gomock" -) - -// MockRetry is a mock of Retry interface. -type MockRetry struct { - ctrl *gomock.Controller - recorder *MockRetryMockRecorder -} - -// MockRetryMockRecorder is the mock recorder for MockRetry. -type MockRetryMockRecorder struct { - mock *MockRetry -} - -// NewMockRetry creates a new mock instance. -func NewMockRetry(ctrl *gomock.Controller) *MockRetry { - mock := &MockRetry{ctrl: ctrl} - mock.recorder = &MockRetryMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRetry) EXPECT() *MockRetryMockRecorder { - return m.recorder -} - -// CalculateNextDelay mocks base method. -func (m *MockRetry) CalculateNextDelay() time.Duration { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CalculateNextDelay") - ret0, _ := ret[0].(time.Duration) - return ret0 -} - -// CalculateNextDelay indicates an expected call of CalculateNextDelay. -func (mr *MockRetryMockRecorder) CalculateNextDelay() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateNextDelay", reflect.TypeOf((*MockRetry)(nil).CalculateNextDelay)) -} diff --git a/libs/backoff/retrypolicy/retrypolicy.go b/libs/backoff/retrypolicy/retrypolicy.go deleted file mode 100644 index 5b64d7347..000000000 --- a/libs/backoff/retrypolicy/retrypolicy.go +++ /dev/null @@ -1,136 +0,0 @@ -package retrypolicy - -import ( - "crypto/rand" - "fmt" - "math" - "math/big" - "time" -) - -// Done is returned when CalculateNextDelay has reached it delay retry limit -const Done time.Duration = -1 - -type ( - // Retry api - Retry interface { - // CalculateNextDelay implementations should return the next delay interval - CalculateNextDelay() time.Duration - } - - policy struct { - startTime time.Time - currentAttempt int - initialInterval time.Duration - backoffCoefficient float64 - maximumInterval time.Duration - expirationInterval time.Duration - maximumAttempt int - } - - // Option func to build retry policy - Option func(policy *policy) error -) - -// New return a new instance of retry policy -func New(options ...Option) (Retry, error) { - retryPolicy := new(policy) - - retryPolicy.startTime = time.Now() - retryPolicy.currentAttempt = 0 - - for _, option := range options { - if err := option(retryPolicy); err != nil { - return nil, fmt.Errorf("error initializing retry policy %w", err) - } - } - - return retryPolicy, nil -} - -// CalculateNextDelay returns the next delay interval based on the retry policy -func (p *policy) CalculateNextDelay() time.Duration { - - if p.currentAttempt >= p.maximumAttempt { - return Done - } - - elapsedTime := time.Since(p.startTime) - - if elapsedTime >= p.expirationInterval { - return Done - } - - nextInterval := float64(p.initialInterval) * math.Pow(p.backoffCoefficient, float64(p.currentAttempt)) - if nextInterval <= 0 { - return Done - } - - if p.maximumInterval != 0 { - nextInterval = math.Min(nextInterval, float64(p.maximumInterval)) - } - - if p.expirationInterval != 0 { - remainingTime := math.Max(0, float64(p.expirationInterval-elapsedTime)) - nextInterval = math.Min(remainingTime, nextInterval) - } - - nextDuration := time.Duration(nextInterval) - if nextDuration < p.initialInterval { - return Done - } - - jitter := int64(0.2 * nextInterval) - if jitter < 1 { - jitter = 1 - } - - n, err := rand.Int(rand.Reader, big.NewInt(jitter)) - if err != nil || n == nil { - panic("panic generating random int for jitter") - } - nextInterval = nextInterval*0.8 + float64(n.Int64()) - - p.currentAttempt++ - return time.Duration(nextInterval) -} - -// WithInitialInterval sets the initial interval -func WithInitialInterval(initialInterval time.Duration) Option { - return func(p *policy) error { - p.initialInterval = initialInterval - return nil - } -} - -// WithBackoffCoefficient sets the coefficient used to calculate next interval -func WithBackoffCoefficient(backoffCoefficient float64) Option { - return func(p *policy) error { - p.backoffCoefficient = backoffCoefficient - return nil - } -} - -// WithMaximumInterval sets the maximum time that can be calculated for next interval -func WithMaximumInterval(maximumInterval time.Duration) Option { - return func(p *policy) error { - p.maximumInterval = maximumInterval - return nil - } -} - -// WithExpirationInterval sets the maximum elapsed time an operation should be tried for -func WithExpirationInterval(expirationInterval time.Duration) Option { - return func(p *policy) error { - p.expirationInterval = expirationInterval - return nil - } -} - -// WithMaximumAttempts sets the maximum number of times an operation will be tried -func WithMaximumAttempts(maximumAttempts int) Option { - return func(p *policy) error { - p.maximumAttempt = maximumAttempts - return nil - } -} diff --git a/libs/backoff/retrypolicy/retrypolicy_test.go b/libs/backoff/retrypolicy/retrypolicy_test.go deleted file mode 100644 index 0855fab70..000000000 --- a/libs/backoff/retrypolicy/retrypolicy_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package retrypolicy - -import ( - "testing" - "time" - - testutils "github.com/brave-intl/bat-go/libs/test" - "github.com/stretchr/testify/assert" -) - -func TestRetryPolicy_New(t *testing.T) { - t.Parallel() - initialInterval := time.Second - backoffCoefficient := float64(testutils.RandomInt()) - maximumInterval := time.Second - expirationInterval := time.Second - maximumAttempts := testutils.RandomInt() - - retryPolicy, err := New( - WithInitialInterval(initialInterval), - WithBackoffCoefficient(backoffCoefficient), - WithMaximumInterval(maximumInterval), - WithExpirationInterval(expirationInterval), - WithMaximumAttempts(maximumAttempts), - ) - - assert.NoError(t, err) - assert.NotNil(t, retryPolicy) -} - -func TestRetryPolicy_CalculateNextDelay_MaxAttempts(t *testing.T) { - t.Parallel() - retryPolicy := policy{ - currentAttempt: 1, - maximumAttempt: 1, - } - assert.Equal(t, Done, retryPolicy.CalculateNextDelay()) -} - -func TestPolicy_CalculateNextDelay_ElapsedTimeGreaterThanExpirationInterval(t *testing.T) { - t.Parallel() - retryPolicy := policy{ - currentAttempt: 0, - maximumAttempt: 10, - expirationInterval: time.Second * 10, - startTime: time.Now().Add(-time.Second * 11), - } - assert.Equal(t, Done, retryPolicy.CalculateNextDelay()) -} - -func TestPolicy_CalculateNextDelay_NextIntervalIsZero(t *testing.T) { - t.Parallel() - retryPolicy := policy{ - currentAttempt: 0, - maximumAttempt: 1, - expirationInterval: time.Second * 10, - startTime: time.Now(), - initialInterval: 0, - } - assert.Equal(t, Done, retryPolicy.CalculateNextDelay()) -} - -func TestPolicy_CalculateNextDelay_Default(t *testing.T) { - t.Parallel() - - durations := []time.Duration{ - 50 * time.Millisecond, - 100 * time.Millisecond, - 200 * time.Millisecond, - 400 * time.Millisecond, - 800 * time.Millisecond, - 1600 * time.Millisecond, - 3200 * time.Millisecond, - 6400 * time.Millisecond, - 10000 * time.Millisecond, - // maximum default policy interval is 10 sec we should not exceed this value - 10000 * time.Millisecond, - Done, - } - - for _, expected := range durations { - actual := DefaultRetry.CalculateNextDelay() - - if expected == Done { - assert.Equal(t, Done, actual) - break - } - - // calculate minimumDuration to account for jitter - minimumDuration := time.Duration(0.8 * float64(expected)) - assert.GreaterOrEqual(t, actual, minimumDuration) - - time.Sleep(actual) - } -} - -func TestPolicy_CalculateNextDelay_NoRetry(t *testing.T) { - t.Parallel() - assert.Equal(t, Done, NoRetry.CalculateNextDelay()) - assert.Equal(t, Done, NoRetry.CalculateNextDelay()) -} diff --git a/libs/clients/bitflyer/client.go b/libs/clients/bitflyer/client.go deleted file mode 100644 index 8883f268f..000000000 --- a/libs/clients/bitflyer/client.go +++ /dev/null @@ -1,579 +0,0 @@ -package bitflyer - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "os" - "runtime/debug" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/go-jose/go-jose/v3/jwt" - "github.com/google/go-querystring/query" - "github.com/shopspring/decimal" -) - -var ( - validSourceFrom = map[string]bool{ - "tipping": true, - "adrewards": true, - "userdrain": true, - } -) - -// Quote returns a quote of BAT prices -type Quote struct { - ProductCode string `json:"product_code"` - MainCurrency string `json:"main_currency"` - SubCurrency string `json:"sub_currency"` - Rate decimal.Decimal `json:"rate"` - PriceToken string `json:"price_token"` -} - -// QuoteQuery holds the query params for the quote -type QuoteQuery struct { - ProductCode string `url:"product_code,omitempty"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (qq *QuoteQuery) GenerateQueryString() (url.Values, error) { - return query.Values(qq) -} - -// WithdrawToDepositIDPayload holds a single withdrawal request -type WithdrawToDepositIDPayload struct { - CurrencyCode string `json:"currency_code"` - Amount float64 `json:"amount"` - DryRun *bool `json:"dry_run,omitempty"` - DepositID string `json:"deposit_id"` - TransferID string `json:"transfer_id"` - SourceFrom string `json:"source_from"` -} - -// WithdrawToDepositIDBulkPayload holds all WithdrawToDepositIDPayload(s) for a single bulk request -type WithdrawToDepositIDBulkPayload struct { - DryRun bool `json:"dry_run"` - Withdrawals []WithdrawToDepositIDPayload `json:"withdrawals"` - PriceToken string `json:"price_token"` - DryRunOption *DryRunOption `json:"dry_run_option,omitempty"` -} - -// CheckStatusPayload holds the transfer id to check -type CheckStatusPayload struct { - TransferID string `json:"transfer_id"` -} - -// CheckBulkStatusPayload holds info for checking the status of a transfer -type CheckBulkStatusPayload struct { - Withdrawals []CheckStatusPayload `json:"withdrawals"` -} - -// TransferIDsToBulkStatus takes a list of transferIDs and turns them into a payload for checking their status -func TransferIDsToBulkStatus(transferIDs []string) CheckBulkStatusPayload { - checkStatusPayload := []CheckStatusPayload{} - for _, transferID := range transferIDs { - checkStatusPayload = append(checkStatusPayload, CheckStatusPayload{ - TransferID: transferID, - }) - } - return CheckBulkStatusPayload{ - Withdrawals: checkStatusPayload, - } -} - -// ToBulkStatus converts an upload to a checks status payload -func (w WithdrawToDepositIDBulkPayload) ToBulkStatus() CheckBulkStatusPayload { - checkStatusPayload := []CheckStatusPayload{} - for _, wd := range w.Withdrawals { - checkStatusPayload = append(checkStatusPayload, CheckStatusPayload{ - TransferID: wd.TransferID, - }) - } - return CheckBulkStatusPayload{ - Withdrawals: checkStatusPayload, - } -} - -// WithdrawToDepositIDResponse holds a single withdrawal request -type WithdrawToDepositIDResponse struct { - CurrencyCode string `json:"currency_code"` - Amount decimal.Decimal `json:"amount"` - Message string `json:"message"` - Status string `json:"transfer_status"` - TransferID string `json:"transfer_id"` -} - -// CategorizeStatus checks the status of a withdrawal response and categorizes it -func (withdrawResponse WithdrawToDepositIDResponse) CategorizeStatus() string { - switch withdrawResponse.Status { - case "SUCCESS", "EXECUTED": - return "complete" - case "NOT_FOUND", "NO_INV", "INVALID_MEMO", "NOT_FOUNTD", "INVALID_AMOUNT", "NOT_ALLOWED_TO_SEND", "NOT_ALLOWED_TO_RECV", "LOCKED_BY_QUICK_DEPOSIT", "SESSION_SEND_LIMIT", "SESSION_TIME_OUT", "EXPIRED", "NOPOSITION", "OTHER_ERROR", "MONTHLY_SEND_LIMIT": - return "failed" - case "CREATED", "PENDING": - return "pending" - } - return "unknown" -} - -// TokenPayload holds the data needed to get a new token -type TokenPayload struct { - GrantType string `json:"grant_type"` - ClientID string `json:"client_id"` - ClientSecret string `json:"client_secret"` - ExtraClientSecret string `json:"extra_client_secret"` -} - -// TokenResponse holds the response from refreshing a token -type TokenResponse struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - ExpiresIn int `json:"expires_in"` - Scope string `json:"scope"` - AccountHash string `json:"account_hash"` - TokenType string `json:"token_type"` -} - -// Inventory holds the balance for a particular currency -type Inventory struct { - CurrencyCode string `json:"currency_code"` - Amount decimal.Decimal `json:"amount"` - Available decimal.Decimal `json:"available"` -} - -// InventoryResponse is the response to a balance inquery -type InventoryResponse struct { - AccountHash string `json:"account_hash"` - Inventory []Inventory `json:"inventory"` -} - -// DryRunOption holds options for dry running a transaction -type DryRunOption struct { - RequestAPITransferStatus string `json:"request_api_transfer_status"` - ProcessTimeSec uint `json:"process_time_sec"` - StatusAPITransferStatus string `json:"status_api_transfer_status"` -} - -// NewWithdrawToDepositIDBulkPayload creates a bulk request -func NewWithdrawToDepositIDBulkPayload(dryRunOptions *DryRunOption, priceToken string, withdrawals *[]WithdrawToDepositIDPayload) *WithdrawToDepositIDBulkPayload { - dryRun := false - if dryRunOptions != nil { - dryRun = true - enum := "ENUM" - if dryRunOptions.RequestAPITransferStatus != enum { - dryRunOptions.RequestAPITransferStatus = enum - } - if dryRunOptions.StatusAPITransferStatus != enum { - dryRunOptions.StatusAPITransferStatus = enum - } - } - return &WithdrawToDepositIDBulkPayload{ - PriceToken: priceToken, - Withdrawals: *withdrawals, - DryRun: dryRun, - DryRunOption: dryRunOptions, - } -} - -// WithdrawToDepositIDBulkResponse holds info about the status of the bulk settlements -type WithdrawToDepositIDBulkResponse struct { - DryRun bool `json:"dry_run"` - Withdrawals []WithdrawToDepositIDResponse `json:"withdrawals"` -} - -// NewWithdrawsFromTxs creates an array of withdrawal requests -func NewWithdrawsFromTxs( - sourceFrom string, - txs []custodian.Transaction, -) (*[]WithdrawToDepositIDPayload, error) { - withdrawals := []WithdrawToDepositIDPayload{} - tolerance := decimal.NewFromFloat(0.00000001) - if !validSourceFrom[sourceFrom] { - return nil, fmt.Errorf("valid `sourceFrom` value must be passed got: `%s`", sourceFrom) - } - for _, tx := range txs { - bat := altcurrency.BAT.FromProbi(tx.Probi) - if bat.Exponent() > 8 { - return nil, fmt.Errorf("cannot convert float exactly, %d", bat) - } - // exact is never true, equality check needed - f64, _ := bat.Float64() - delta := decimal.NewFromFloat(f64).Sub(bat).Abs() - if delta.GreaterThan(tolerance) { - return nil, fmt.Errorf("bat conversion did not work: %.8f is not equal %d", f64, bat) - } - withdrawals = append(withdrawals, WithdrawToDepositIDPayload{ - CurrencyCode: "BAT", - Amount: f64, - DepositID: tx.Destination, - TransferID: tx.BitflyerTransferID(), - SourceFrom: sourceFrom, - }) - } - return &withdrawals, nil -} - -// Client abstracts over the underlying client -type Client interface { - // FetchQuote gets a quote of BAT to JPY - FetchQuote(ctx context.Context, productCode string, readFromFile bool) (*Quote, error) - // UploadBulkPayout posts a signed bulk layout to bitflyer - UploadBulkPayout(ctx context.Context, payload WithdrawToDepositIDBulkPayload) (*WithdrawToDepositIDBulkResponse, error) - // CheckPayoutStatus checks the status of a transaction - CheckPayoutStatus(ctx context.Context, payload CheckBulkStatusPayload) (*WithdrawToDepositIDBulkResponse, error) - // CheckInventory check available balance of bitflyer account - CheckInventory(ctx context.Context) (map[string]Inventory, error) - // RefreshToken refreshes the token belonging to the provided secret values - RefreshToken(ctx context.Context, payload TokenPayload) (*TokenResponse, error) - // SetAuthToken sets the auth token on underlying client object - SetAuthToken(authToken string) - // FetchBalance requests balance information for the auth token on the underlying client object - FetchBalance(ctx context.Context) (*InventoryResponse, error) -} - -// HTTPClient wraps http.Client for interacting with the cbr server -type HTTPClient struct { - client *clients.SimpleHTTPClient -} - -var ( - // ErrInvalidServerURL - invalid server url - ErrInvalidServerURL = errors.New("invalid bitflyer server url") - // ErrInvalidToken - invalid server token - ErrInvalidToken = errors.New("invalid bitflyer token") -) - -// NewWithContext returns a new HTTPClient, retrieving the base URL from the ctx -func NewWithContext(ctx context.Context) (Client, error) { - // get the server url - serverURL, ok := ctx.Value(appctx.BitflyerServerURLCTXKey).(string) - if !ok || len(serverURL) == 0 { - return nil, ErrInvalidServerURL - } - - // get the proxy url if applicable - proxyURL, ok := ctx.Value(appctx.BitflyerProxyURLCTXKey).(string) - if !ok || len(proxyURL) == 0 { - proxyURL = "" - } - - // get the token - token, ok := ctx.Value(appctx.BitflyerTokenCTXKey).(string) - if !ok || len(token) == 0 { - token = "" - } - - // create the client - client, err := clients.NewWithProxy("bitflyer", serverURL, token, proxyURL) - if err != nil { - return nil, err - } - // return instrumented client - return NewClientWithPrometheus(&HTTPClient{client}, "bitflyer_client"), err -} - -// New returns a new HTTPClient, retrieving the base URL from the environment -func New() (Client, error) { - serverEnvKey := "BITFLYER_SERVER" - serverURL := os.Getenv(serverEnvKey) - if len(serverURL) == 0 { - return nil, errors.New(serverEnvKey + " was empty") - } - proxy := os.Getenv("HTTP_PROXY") - client, err := clients.NewWithProxy("bitflyer", serverURL, os.Getenv("BITFLYER_TOKEN"), proxy) - if err != nil { - return nil, err - } - return NewClientWithPrometheus(&HTTPClient{client}, "bitflyer_client"), err -} - -// SetAuthToken sets the auth token -func (c *HTTPClient) SetAuthToken(authToken string) { - c.client.AuthToken = authToken -} - -// FetchQuote fetches prices for determining constraints -func (c *HTTPClient) FetchQuote( - ctx context.Context, - productCode string, - readFromFile bool, -) (*Quote, error) { - if readFromFile { - read, err := readQuoteFromFile() - if err != nil { - fmt.Println("failed to read quote from file", err) - return nil, err - } - if withinPriceTokenExpiration(read) { - return &read.Body, nil - } - } - req, err := c.client.NewRequest(ctx, "GET", "/api/link/v1/getprice", nil, &QuoteQuery{ - ProductCode: productCode, - }) - if err != nil { - return nil, err - } - - var body Quote - resp, err := c.client.Do(ctx, req, &body) - if err == nil { - expiry, err := parseExpiry(body.PriceToken) - if err == nil { - writeQuoteToFile(SavedQuote{ - Body: body, - Expiry: *expiry, - }) - } - } - return &body, handleBitflyerError(ctx, err, resp) -} - -// PriceTokenInfo holds info from the price token -type PriceTokenInfo struct { - ProductCode string `json:"product_code,omitempty"` - Rate decimal.Decimal `json:"rate,omitempty"` - IssuedAt int `json:"iat,omitempty"` - Expiry int `json:"exp,omitempty"` -} - -func parseExpiry(token string) (*time.Time, error) { - var claims map[string]interface{} - parsed, err := jwt.ParseSigned(token) - if err != nil { - return nil, err - } - err = parsed.UnsafeClaimsWithoutVerification(&claims) - if err != nil { - return nil, err - } - exp := claims["exp"].(float64) - ts := time.Unix(int64(exp), 0) - return &ts, nil -} - -func withinPriceTokenExpiration(savedQuote *SavedQuote) bool { - if savedQuote == nil { - return false - } - return time.Now().Before(savedQuote.Expiry) -} - -func writeQuoteToFile(quote SavedQuote) { - data, err := json.Marshal(quote) - if err != nil { - fmt.Println("marshal error", err) - return - } - _ = ioutil.WriteFile("./fetch-quote.json", data, 0777) -} - -// SavedQuote stores a quote locally -type SavedQuote struct { - Body Quote `json:"body"` - Expiry time.Time `json:"expiry"` -} - -func readQuoteFromFile() (*SavedQuote, error) { - dat, err := ioutil.ReadFile("./fetch-quote.json") - if err != nil { - fmt.Println("read file error", err) - return nil, nil - } - var body SavedQuote - err = json.Unmarshal(dat, &body) - if err != nil { - return nil, fmt.Errorf("unmarshal quote file error: %w", err) - } - return &body, nil -} - -// UploadBulkPayout uploads payouts to bitflyer -func (c *HTTPClient) UploadBulkPayout(ctx context.Context, payload WithdrawToDepositIDBulkPayload) (*WithdrawToDepositIDBulkResponse, error) { - req, err := c.client.NewRequest(ctx, http.MethodPost, "/api/link/v1/coin/withdraw-to-deposit-id/bulk-request", payload, nil) - if err != nil { - return nil, err - } - c.setupRequestHeaders(req) - - var withdrawToDepositIDBulkResponse WithdrawToDepositIDBulkResponse - resp, err := c.client.Do(ctx, req, &withdrawToDepositIDBulkResponse) - - return &withdrawToDepositIDBulkResponse, handleBitflyerError(ctx, err, resp) -} - -// CheckPayoutStatus checks bitflyer transaction status -func (c *HTTPClient) CheckPayoutStatus(ctx context.Context, payload CheckBulkStatusPayload) (*WithdrawToDepositIDBulkResponse, error) { - - req, err := c.client.NewRequest(ctx, http.MethodPost, "/api/link/v1/coin/withdraw-to-deposit-id/bulk-status", payload, nil) - if err != nil { - return nil, err - } - c.setupRequestHeaders(req) - - var body WithdrawToDepositIDBulkResponse - resp, err := c.client.Do(ctx, req, &body) - - return &body, handleBitflyerError(ctx, err, resp) -} - -// RefreshToken gets a new token from bitflyer -func (c *HTTPClient) RefreshToken(ctx context.Context, payload TokenPayload) (*TokenResponse, error) { - - defer func() { - if r := recover(); r != nil { - logging.FromContext(ctx).Error(). - Str("panic", fmt.Sprintf("%+v", r)). - Str("stacktrace", string(debug.Stack())). - Msg("failed to get bitflyer refresh token") - } - }() - - req, err := c.client.NewRequest(ctx, http.MethodPost, "/api/link/v1/token", payload, nil) - if err != nil { - return nil, fmt.Errorf("failed to generate refresh token request: %w", err) - } - c.setupRequestHeaders(req) - - var body TokenResponse - resp, err := c.client.Do(ctx, req, &body) - if err != nil { - return &body, fmt.Errorf("failed to execute refresh token request: %w", err) - } - c.SetAuthToken(body.AccessToken) - - return &body, handleBitflyerError(ctx, err, resp) -} - -// CheckInventory fetches the current balances of an account -func (c *HTTPClient) CheckInventory( - ctx context.Context, -) (map[string]Inventory, error) { - logger := logging.Logger(ctx, "bitflyer.CheckInventory") - - defer func() { - if r := recover(); r != nil { - logger.Error().Str("panic", fmt.Sprintf("%+v", r)).Msg("failed to check inventory") - } - }() - logger.Info(). - Msg("Calling account inventory") - - req, err := c.client.NewRequest(ctx, http.MethodGet, "/api/link/v1/account/inventory", nil, nil) - if err != nil { - return nil, err - } - c.setupRequestHeaders(req) - - var body InventoryResponse - resp, err := c.client.Do(ctx, req, &body) - err = handleBitflyerError(ctx, err, resp) - if err != nil { - return nil, err - } - output := make(map[string]Inventory) - - for _, inv := range body.Inventory { - output[inv.CurrencyCode] = inv - } - - logger.Info(). - Str("Account Hash", body.AccountHash). - Str("Available JPY", output["JPY"].Available.String()). - Str("Available BAT", output["BAT"].Available.String()). - Msg("using updated token. make sure this value is in your env vars (BITFLYER_TOKEN) to avoid refreshes") - return output, err -} - -func (c *HTTPClient) setupRequestHeaders(req *http.Request) { - req.Header.Set("authorization", "Bearer "+c.client.AuthToken) - req.Header.Set("content-type", "application/json") -} - -func handleBitflyerError(ctx context.Context, e error, resp *http.Response) error { - if resp == nil { - return e - } - - // if this is not a bitflyer error just return err passed in - if resp.StatusCode <= 299 { - return e - } - - b, err := requestutils.Read(ctx, resp.Body) - if err != nil { - return fmt.Errorf("failed to read body of bitflyer response to handle err: %w", err) - } - - var bfError *clients.BitflyerError - if len(b) != 0 { - err = json.Unmarshal(b, &bfError) - if err != nil { - return err - } - } - - if len(bfError.Label) == 0 { - return e - } - - // put the protocol status code on the error too - bfError.HTTPStatusCode = resp.StatusCode - - return bfError -} - -// TokenPayloadFromCtx - given some context, create our bf token payload -func TokenPayloadFromCtx(ctx context.Context) TokenPayload { - // get logger from context - logger := logging.Logger(ctx, "bitflyer.TokenPayloadFromCtx") - // get bf creds from context - clientID, err := appctx.GetStringFromContext(ctx, appctx.BitflyerClientIDCTXKey) - if err != nil { - // misconfigured, needs client id - logger.Error().Err(err).Msg("missing bitflyer client id from ctx") - } - clientSecret, err := appctx.GetStringFromContext(ctx, appctx.BitflyerClientSecretCTXKey) - if err != nil { - // misconfigured, needs client Secret - logger.Error().Err(err).Msg("missing bitflyer client Secret from ctx") - } - extraClientSecret, err := appctx.GetStringFromContext(ctx, appctx.BitflyerExtraClientSecretCTXKey) - if err != nil { - // misconfigured, needs extra client secret - logger.Error().Err(err).Msg("missing bitflyer extra client Secret from ctx") - } - return TokenPayload{ - GrantType: "client_credentials", - ClientID: clientID, - ClientSecret: clientSecret, - ExtraClientSecret: extraClientSecret, - } -} - -// FetchBalance requests balance information for the auth token on the underlying client object -func (c *HTTPClient) FetchBalance(ctx context.Context) (*InventoryResponse, error) { - request, err := c.client.NewRequest(ctx, http.MethodGet, "api/link/v1/account/inventory", nil, nil) - if err != nil { - return nil, fmt.Errorf("fetch balance error: could not create request: %w", err) - } - c.setupRequestHeaders(request) - - var inventoryResponse *InventoryResponse - response, err := c.client.Do(ctx, request, &inventoryResponse) - if err != nil { - return nil, fmt.Errorf("fetch balance error: could not execute request: %w", err) - } - - return inventoryResponse, handleBitflyerError(ctx, err, response) -} diff --git a/libs/clients/bitflyer/client_test.go b/libs/clients/bitflyer/client_test.go deleted file mode 100644 index b37111a25..000000000 --- a/libs/clients/bitflyer/client_test.go +++ /dev/null @@ -1,115 +0,0 @@ -//go:build integration && vpn -// +build integration,vpn - -package bitflyer - -import ( - "bytes" - "context" - "errors" - "github.com/stretchr/testify/assert" - "net/http" - "os" - "testing" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/custodian" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type BitflyerTestSuite struct { - suite.Suite - secret cryptography.HMACKey -} - -func TestBitflyerTestSuite(t *testing.T) { - suite.Run(t, new(BitflyerTestSuite)) -} - -func (suite *BitflyerTestSuite) SetupTest() { -} - -func (suite *BitflyerTestSuite) TestBulkPay() { - ctx := context.Background() - client, err := New() - suite.Require().NoError(err, "Must be able to correctly initialize the client") - one := decimal.NewFromFloat(1) - - quote, err := client.FetchQuote(ctx, "BAT_JPY", true) - suite.Require().NoError(err, "fetching a quote does not fail") - - dryRun := &DryRunOption{} - tx := custodian.Transaction{ - SettlementID: uuid.NewV4().String(), - Destination: os.Getenv("BITFLYER_TEST_DESTINATION_ID"), - Channel: "brave.com", - Probi: altcurrency.BAT.ToProbi(one), - Amount: one, - } - sourceFrom := os.Getenv("BITFLYER_SOURCE_FROM") - if sourceFrom == "" { - sourceFrom = "tipping" - } - txs := []custodian.Transaction{tx} - withdrawals, err := NewWithdrawsFromTxs(sourceFrom, txs) - suite.Require().NoError(err) - bulkTransferRequest := NewWithdrawToDepositIDBulkPayload( - dryRun, - quote.PriceToken, - withdrawals, - ) - - bulkPayoutResponse, err := client.UploadBulkPayout(ctx, *bulkTransferRequest) - expectedPayoutResults := WithdrawToDepositIDBulkResponse{ - Withdrawals: []WithdrawToDepositIDResponse{ - { - TransferID: tx.TransferID(), - Amount: one, - Message: "", - Status: "SUCCESS", - CurrencyCode: "BAT", - }, - }, - } - suite.Require().NoError(err, "should not error during bulk payout uploading") - suite.Require().Equal(&expectedPayoutResults, bulkPayoutResponse, "the response should be predictable") -} - -type nopCloser struct { - *bytes.Buffer -} - -func (nc nopCloser) Close() error { - return nil -} - -func TestHandleBitflyerError(t *testing.T) { - buf := bytes.NewBufferString(` -{ - "status": -1, - "label": "JsonError.TOKEN_ERROR", - "message": "認証に失敗しました。", - "errors": [ - "242503" - ] -} - `) - body := nopCloser{buf} - resp := http.Response{ - StatusCode: http.StatusUnauthorized, - Body: body, - } - - err := handleBitflyerError(context.Background(), errors.New("failed"), &resp) - var bfError *clients.BitflyerError - if errors.As(err, &bfError) { - assert.Equal(t, bfError.HTTPStatusCode, http.StatusUnauthorized, "status should match") - assert.Equal(t, bfError.Status, -1, "status should match") - } else { - assert.Fail(t, "should not be another type of error") - } -} diff --git a/libs/clients/bitflyer/instrumented_client.go b/libs/clients/bitflyer/instrumented_client.go deleted file mode 100755 index f3a9d1674..000000000 --- a/libs/clients/bitflyer/instrumented_client.go +++ /dev/null @@ -1,134 +0,0 @@ -package bitflyer - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "bitflyer_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// CheckInventory implements Client -func (_d ClientWithPrometheus) CheckInventory(ctx context.Context) (m1 map[string]Inventory, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CheckInventory", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CheckInventory(ctx) -} - -// CheckPayoutStatus implements Client -func (_d ClientWithPrometheus) CheckPayoutStatus(ctx context.Context, payload CheckBulkStatusPayload) (wp1 *WithdrawToDepositIDBulkResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CheckPayoutStatus", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CheckPayoutStatus(ctx, payload) -} - -// FetchBalance implements Client -func (_d ClientWithPrometheus) FetchBalance(ctx context.Context) (ip1 *InventoryResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchBalance", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchBalance(ctx) -} - -// FetchQuote implements Client -func (_d ClientWithPrometheus) FetchQuote(ctx context.Context, productCode string, readFromFile bool) (qp1 *Quote, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchQuote", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchQuote(ctx, productCode, readFromFile) -} - -// RefreshToken implements Client -func (_d ClientWithPrometheus) RefreshToken(ctx context.Context, payload TokenPayload) (tp1 *TokenResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "RefreshToken", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RefreshToken(ctx, payload) -} - -// SetAuthToken implements Client -func (_d ClientWithPrometheus) SetAuthToken(authToken string) { - _since := time.Now() - defer func() { - result := "ok" - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "SetAuthToken", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.SetAuthToken(authToken) - return -} - -// UploadBulkPayout implements Client -func (_d ClientWithPrometheus) UploadBulkPayout(ctx context.Context, payload WithdrawToDepositIDBulkPayload) (wp1 *WithdrawToDepositIDBulkResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "UploadBulkPayout", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UploadBulkPayout(ctx, payload) -} diff --git a/libs/clients/bitflyer/mock/mock.go b/libs/clients/bitflyer/mock/mock.go deleted file mode 100644 index 452e6caaa..000000000 --- a/libs/clients/bitflyer/mock/mock.go +++ /dev/null @@ -1,138 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/bitflyer/client.go - -// Package mock_bitflyer is a generated GoMock package. -package mock_bitflyer - -import ( - context "context" - reflect "reflect" - - bitflyer "github.com/brave-intl/bat-go/libs/clients/bitflyer" - gomock "github.com/golang/mock/gomock" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// CheckInventory mocks base method. -func (m *MockClient) CheckInventory(ctx context.Context) (map[string]bitflyer.Inventory, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckInventory", ctx) - ret0, _ := ret[0].(map[string]bitflyer.Inventory) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CheckInventory indicates an expected call of CheckInventory. -func (mr *MockClientMockRecorder) CheckInventory(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckInventory", reflect.TypeOf((*MockClient)(nil).CheckInventory), ctx) -} - -// CheckPayoutStatus mocks base method. -func (m *MockClient) CheckPayoutStatus(ctx context.Context, payload bitflyer.CheckBulkStatusPayload) (*bitflyer.WithdrawToDepositIDBulkResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckPayoutStatus", ctx, payload) - ret0, _ := ret[0].(*bitflyer.WithdrawToDepositIDBulkResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CheckPayoutStatus indicates an expected call of CheckPayoutStatus. -func (mr *MockClientMockRecorder) CheckPayoutStatus(ctx, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPayoutStatus", reflect.TypeOf((*MockClient)(nil).CheckPayoutStatus), ctx, payload) -} - -// FetchBalance mocks base method. -func (m *MockClient) FetchBalance(ctx context.Context) (*bitflyer.InventoryResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchBalance", ctx) - ret0, _ := ret[0].(*bitflyer.InventoryResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchBalance indicates an expected call of FetchBalance. -func (mr *MockClientMockRecorder) FetchBalance(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchBalance", reflect.TypeOf((*MockClient)(nil).FetchBalance), ctx) -} - -// FetchQuote mocks base method. -func (m *MockClient) FetchQuote(ctx context.Context, productCode string, readFromFile bool) (*bitflyer.Quote, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchQuote", ctx, productCode, readFromFile) - ret0, _ := ret[0].(*bitflyer.Quote) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchQuote indicates an expected call of FetchQuote. -func (mr *MockClientMockRecorder) FetchQuote(ctx, productCode, readFromFile interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchQuote", reflect.TypeOf((*MockClient)(nil).FetchQuote), ctx, productCode, readFromFile) -} - -// RefreshToken mocks base method. -func (m *MockClient) RefreshToken(ctx context.Context, payload bitflyer.TokenPayload) (*bitflyer.TokenResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RefreshToken", ctx, payload) - ret0, _ := ret[0].(*bitflyer.TokenResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RefreshToken indicates an expected call of RefreshToken. -func (mr *MockClientMockRecorder) RefreshToken(ctx, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshToken", reflect.TypeOf((*MockClient)(nil).RefreshToken), ctx, payload) -} - -// SetAuthToken mocks base method. -func (m *MockClient) SetAuthToken(authToken string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetAuthToken", authToken) -} - -// SetAuthToken indicates an expected call of SetAuthToken. -func (mr *MockClientMockRecorder) SetAuthToken(authToken interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAuthToken", reflect.TypeOf((*MockClient)(nil).SetAuthToken), authToken) -} - -// UploadBulkPayout mocks base method. -func (m *MockClient) UploadBulkPayout(ctx context.Context, payload bitflyer.WithdrawToDepositIDBulkPayload) (*bitflyer.WithdrawToDepositIDBulkResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UploadBulkPayout", ctx, payload) - ret0, _ := ret[0].(*bitflyer.WithdrawToDepositIDBulkResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UploadBulkPayout indicates an expected call of UploadBulkPayout. -func (mr *MockClientMockRecorder) UploadBulkPayout(ctx, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadBulkPayout", reflect.TypeOf((*MockClient)(nil).UploadBulkPayout), ctx, payload) -} diff --git a/libs/clients/cbr/client.go b/libs/clients/cbr/client.go deleted file mode 100644 index d627b72c5..000000000 --- a/libs/clients/cbr/client.go +++ /dev/null @@ -1,270 +0,0 @@ -package cbr - -import ( - "context" - "errors" - "fmt" - "net/http" - "os" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - errorutils "github.com/brave-intl/bat-go/libs/errors" -) - -// Client abstracts over the underlying client -type Client interface { - // CreateIssuer creates an issuer. - CreateIssuer(ctx context.Context, issuer string, maxTokens int) error - // CreateIssuerV3 creates a version 3 issuer. - CreateIssuerV3(ctx context.Context, createIssuerV3 IssuerRequest) error - // GetIssuer returns issuers prior to version 3. - GetIssuer(ctx context.Context, issuer string) (*IssuerResponse, error) - // GetIssuerV3 returns issuers based on issuer name. Should be used when retrieving version 3 issuers. - GetIssuerV3(ctx context.Context, issuer string) (*IssuerResponse, error) - SignCredentials(ctx context.Context, issuer string, creds []string) (*CredentialsIssueResponse, error) - RedeemCredential(ctx context.Context, issuer string, preimage string, signature string, payload string) error - RedeemCredentials(ctx context.Context, credentials []CredentialRedemption, payload string) error - RedeemCredentialV3(ctx context.Context, issuer string, preimage string, signature string, payload string) error -} - -// HTTPClient wraps http.Client for interacting with the cbr server -type HTTPClient struct { - client *clients.SimpleHTTPClient -} - -// New returns a new HTTPClient, retrieving the base URL from the environment -func New() (Client, error) { - serverEnvKey := "CHALLENGE_BYPASS_SERVER" - serverURL := os.Getenv("CHALLENGE_BYPASS_SERVER") - if len(serverURL) == 0 { - return nil, errors.New(serverEnvKey + " was empty") - } - client, err := clients.New(serverURL, os.Getenv("CHALLENGE_BYPASS_TOKEN")) - if err != nil { - return nil, err - } - return NewClientWithPrometheus(&HTTPClient{client}, "cbr_client"), err -} - -// IssuerCreateRequest is a request to create a new issuer -type IssuerCreateRequest struct { - Name string `json:"name"` - MaxTokens int `json:"max_tokens"` -} - -// IssuerResponse contains detais about a newly created or fetched issuer -type IssuerResponse struct { - Name string `json:"name"` - PublicKey string `json:"public_key"` - ExpiresAt string `json:"expires_at,omitempty"` - Cohort int16 `json:"cohort,omitempty"` -} - -// CreateIssuer with the provided name and token cap -func (c *HTTPClient) CreateIssuer(ctx context.Context, issuer string, maxTokens int) error { - req, err := c.client.NewRequest(ctx, "POST", "v1/issuer/", &IssuerCreateRequest{Name: issuer, MaxTokens: maxTokens}, nil) - if err != nil { - return err - } - - _, err = c.client.Do(ctx, req, nil) - - return err -} - -// IssuerRequest - create a new issuer request structure -type IssuerRequest struct { - Name string `json:"name"` - Version int `json:"version"` - Cohort int16 `json:"cohort"` - MaxTokens int `json:"max_tokens"` - ValidFrom *time.Time `json:"valid_from"` // start of issuance - ExpiresAt *time.Time `json:"expires_at"` - Duration string `json:"duration"` // valid duration of each sub issuer - Buffer int `json:"buffer"` // number of sub issuers in the future - Overlap int `json:"overlap"` // number of days sub issuer should overlap -} - -// CreateIssuerV3 creates a version 3 issuer. -func (c *HTTPClient) CreateIssuerV3(ctx context.Context, issuerRequest IssuerRequest) error { - issuerRequest.Version = 3 - req, err := c.client.NewRequest(ctx, http.MethodPost, "v3/issuer/", issuerRequest, nil) - if err != nil { - return fmt.Errorf("error creating create issuer request v3: %w", err) - } - - _, err = c.client.Do(ctx, req, nil) - - return err -} - -// GetIssuer by name -func (c *HTTPClient) GetIssuer(ctx context.Context, issuer string) (*IssuerResponse, error) { - req, err := c.client.NewRequest(ctx, "GET", "v1/issuer/"+issuer, nil, nil) - if err != nil { - return nil, err - } - - var resp IssuerResponse - _, err = c.client.Do(ctx, req, &resp) - - return &resp, err -} - -// CredentialsIssueRequest is a request to issue more tokens -type CredentialsIssueRequest struct { - BlindedTokens []string `json:"blinded_tokens"` -} - -// CredentialsIssueResponse contains the signed tokens and batch proof -type CredentialsIssueResponse struct { - BatchProof string `json:"batch_proof"` - SignedTokens []string `json:"signed_tokens"` -} - -// GetIssuerV3 returns issuers based on issuer name. Should be used when retrieving version 3 issuers. -func (c *HTTPClient) GetIssuerV3(ctx context.Context, issuer string) (*IssuerResponse, error) { - req, err := c.client.NewRequest(ctx, http.MethodGet, "v3/issuer/"+issuer, nil, nil) - if err != nil { - return nil, err - } - - var resp IssuerResponse - _, err = c.client.Do(ctx, req, &resp) - - return &resp, err -} - -// SignCredentials using a particular issuer -func (c *HTTPClient) SignCredentials(ctx context.Context, issuer string, creds []string) (*CredentialsIssueResponse, error) { - req, err := c.client.NewRequest(ctx, "POST", "v1/blindedToken/"+issuer, &CredentialsIssueRequest{BlindedTokens: creds}, nil) - if err != nil { - return nil, err - } - - var resp CredentialsIssueResponse - _, err = c.client.Do(ctx, req, &resp) - - return &resp, err -} - -// CredentialRedeemRequest is a request to redeem a single token toward some payload -type CredentialRedeemRequest struct { - TokenPreimage string `json:"t"` - Signature string `json:"signature"` - Payload string `json:"payload"` -} - -var ( - // ErrDupRedeem - Error for duplicate redemptions - ErrDupRedeem = errors.New("cbr duplicate redemption") - // ErrBadRequest - Error for cbr bad requests - ErrBadRequest = errors.New("cbr bad request") -) - -func handleRedeemError(err error) error { - var eb *errorutils.ErrorBundle - if errors.As(err, &eb) { - if hs, ok := eb.Data().(clients.HTTPState); ok { - // possible cbr errors: - // 409 - never retry (already redeemed) - // 400 - never retry (bad request) - // 5xx - retry later - // 429/404 - retry later - switch hs.Status { - case http.StatusConflict: - return errorutils.New(err, ErrDupRedeem.Error(), - errorutils.Codified{ - ErrCode: "cbr_dup_redeem", - Retry: false, - }) - case http.StatusBadRequest: - return errorutils.New(err, ErrBadRequest.Error(), - errorutils.Codified{ - ErrCode: "cbr_bad_request", - Retry: false, - }) - case http.StatusTooManyRequests: - return errorutils.New(err, "cbr rate limit", - errorutils.Codified{ - ErrCode: "cbr_rate_limit", - Retry: true, - }) - case http.StatusNotFound: - return errorutils.New(err, "cbr route not found", - errorutils.Codified{ - ErrCode: "cbr_path_not_found", - Retry: true, - }) - case http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable: - return errorutils.New(err, "cbr internal server error", - errorutils.Codified{ - ErrCode: "cbr_server_err", - Retry: true, - }) - default: - return errorutils.New(err, "cbr unknown cbr result", - errorutils.Codified{ - ErrCode: "cbr_unknown", - Retry: false, - }) - } - } - } - if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { - return errorutils.New(err, "cbr timeout", - errorutils.Codified{ - ErrCode: "cbr_timeout", - Retry: true, - }) - } - return err -} - -// RedeemCredential that was issued by the specified issuer -func (c *HTTPClient) RedeemCredential(ctx context.Context, issuer string, preimage string, signature string, payload string) error { - req, err := c.client.NewRequest(ctx, "POST", "v1/blindedToken/"+issuer+"/redemption/", &CredentialRedeemRequest{TokenPreimage: preimage, Signature: signature, Payload: payload}, nil) - if err != nil { - return err - } - - _, err = c.client.Do(ctx, req, nil) - return handleRedeemError(err) -} - -// CredentialRedemption includes info needed to redeem a single token -type CredentialRedemption struct { - Issuer string `json:"issuer"` - TokenPreimage string `json:"t"` - Signature string `json:"signature"` -} - -// CredentialsRedeemRequest is a request to redeem one or more tokens toward some payload -type CredentialsRedeemRequest struct { - Credentials []CredentialRedemption `json:"tokens"` - Payload string `json:"payload"` -} - -// RedeemCredentials that were issued by the specified issuer -func (c *HTTPClient) RedeemCredentials(ctx context.Context, credentials []CredentialRedemption, payload string) error { - req, err := c.client.NewRequest(ctx, "POST", "v1/blindedToken/bulk/redemption/", &CredentialsRedeemRequest{Credentials: credentials, Payload: payload}, nil) - if err != nil { - return err - } - - _, err = c.client.Do(ctx, req, nil) - return handleRedeemError(err) -} - -// RedeemCredentialV3 redeems a version 3 token that was issued by the specified issuer -func (c *HTTPClient) RedeemCredentialV3(ctx context.Context, issuer string, preimage string, signature string, payload string) error { - req, err := c.client.NewRequest(ctx, "POST", "v3/blindedToken/"+issuer+"/redemption/", - &CredentialRedeemRequest{TokenPreimage: preimage, Signature: signature, Payload: payload}, nil) - if err != nil { - return err - } - - _, err = c.client.Do(ctx, req, nil) - return handleRedeemError(err) -} diff --git a/libs/clients/cbr/client_test.go b/libs/clients/cbr/client_test.go deleted file mode 100644 index a81f3a78c..000000000 --- a/libs/clients/cbr/client_test.go +++ /dev/null @@ -1,263 +0,0 @@ -//go:build integration - -package cbr - -import ( - "context" - "database/sql" - "errors" - "fmt" - "net/http" - "os" - "testing" - "time" - - "github.com/aws/aws-sdk-go/aws/session" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/dynamodb" - "github.com/brave-intl/bat-go/libs/clients" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/ptr" - "github.com/brave-intl/bat-go/libs/test" - _ "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/assert" -) - -func TestCreateIssuerV1(t *testing.T) { - ctx := context.Background() - - client, err := New() - assert.NoError(t, err, "Must be able to correctly initialize the client") - - err = client.CreateIssuer(ctx, "test:"+uuid.NewV4().String(), 100) - assert.NoError(t, err, "Should be able to create issuer") -} - -func TestGetIssuerV1(t *testing.T) { - ctx := context.Background() - - client, err := New() - assert.NoError(t, err, "Must be able to correctly initialize the client") - - issuerName := "test:" + uuid.NewV4().String() - - issuer, err := client.GetIssuer(ctx, issuerName) - assert.Error(t, err, "Should not be able to get issuer") - // checking the error - httpError, ok := err.(*errorutils.ErrorBundle) - assert.Equal(t, true, ok, "should be able to coerce to an error bundle") - httpState, ok := httpError.Data().(clients.HTTPState) - assert.Equal(t, true, ok, "should contain an HTTPState") - assert.Equal(t, http.StatusNotFound, httpState.Status, "status should be not found") - - err = client.CreateIssuer(ctx, issuerName, 100) - assert.NoError(t, err, "Should be able to create issuer") - - issuer, err = client.GetIssuer(ctx, issuerName) - assert.NoError(t, err, "Should be able to get issuer") - - assert.NotEqual(t, len(issuer.PublicKey), 0, "Should have public key") -} - -func TestSignAndRedeemCredentialsV1(t *testing.T) { - databaseURL := os.Getenv("CHALLENGE_BYPASS_DATABASE_URL") - - sKey := "fzJbqh6l/xWAjT6Ulmu+/Taxz8XZ7SDnJ/dUXPgtnQE=" - pKey := "jKj71sdk2XYMwZNSxvUfNkSNCUQeBuUxuTbdjIbupmE=" - blindedToken := "yoGo7zfMr5vAzwyyFKwoFEsUcyUlXKY75VvWLfYi7go=" - signedToken := "ohwnBITMSphAFK/06LtbC+PYl6PmmEhOdybvsfqZjG4=" - preimage := "Aa61pQzyxsy3Z6tSwccnOqiW23fNYp0z3xw6XGlA5FG8O/EqlxR87DWnas49U2JUau44dpiveAt7kBXDH5RjPQ==" - sig := "zx1zdMhN4Et8WnrkVQOad6xhUBAJ7Pq4b8A0n96CRE0QdAQ+tJe0/eFiJqIPMuKkyfQ6VncIkGj9VzkByh9uFA==" - payload := "test message" - - issuerName := "constant" - - db, err := sql.Open("postgres", databaseURL) - if err != nil { - assert.NoError(t, err, "Must be able to connect to challenge-bypass db") - } - - _, err = db.Exec("DELETE from v3_issuer_keys; DELETE FROM v3_issuers; DELETE from redemptions") - assert.NoError(t, err, "Must be able to clear issuers") - - ctx := context.Background() - - client, err := New() - assert.NoError(t, err, "Must be able to correctly initialize the client") - - err = client.CreateIssuer(ctx, issuerName, 100) - assert.NoError(t, err, "Should be able to create issuer") - - _, err = db.Exec("update v3_issuer_keys set signing_key=$1", sKey) - assert.NoError(t, err, "Must be able to insert issuer key") - - issuer, err := client.GetIssuer(ctx, issuerName) - assert.NoError(t, err, "Should be able to get issuer") - assert.Equal(t, issuer.PublicKey, pKey, "Public key should match expected") - - resp, err := client.SignCredentials(ctx, issuerName, []string{blindedToken}) - assert.NoError(t, err, "Should be able to sign tokens") - assert.Equal(t, resp.SignedTokens[0], signedToken, "Public key should match expected") - - err = client.RedeemCredential(ctx, issuerName, preimage, sig, payload) - assert.NoError(t, err, "Should be able to redeem tokens") - - _, err = db.Exec("DELETE from redemptions") - assert.NoError(t, err, "Must be able to clear redemptions") - - err = client.RedeemCredentials(ctx, []CredentialRedemption{{Issuer: issuerName, TokenPreimage: preimage, Signature: sig}}, payload) - assert.NoError(t, err, "Should be able to bulk redeem tokens") -} - -func TestCreateIssuerV3(t *testing.T) { - client, err := New() - assert.NoError(t, err) - - request := IssuerRequest{ - Name: test.RandomString(), - Cohort: int16(test.RandomNonZeroInt(10)), - MaxTokens: test.RandomNonZeroInt(10), - ValidFrom: ptr.FromTime(time.Now()), - Duration: "P1M", - Buffer: test.RandomNonZeroInt(10), - Overlap: test.RandomNonZeroInt(10), - } - - err = client.CreateIssuerV3(context.Background(), request) - assert.NoError(t, err) -} - -func TestSignAndRedeemCredentialsV3(t *testing.T) { - databaseURL := os.Getenv("CHALLENGE_BYPASS_DATABASE_URL") - - sKey := "fzJbqh6l/xWAjT6Ulmu+/Taxz8XZ7SDnJ/dUXPgtnQE=" - blindedToken := "yoGo7zfMr5vAzwyyFKwoFEsUcyUlXKY75VvWLfYi7go=" - signedToken := "ohwnBITMSphAFK/06LtbC+PYl6PmmEhOdybvsfqZjG4=" - preimage := "Aa61pQzyxsy3Z6tSwccnOqiW23fNYp0z3xw6XGlA5FG8O/EqlxR87DWnas49U2JUau44dpiveAt7kBXDH5RjPQ==" - sig := "zx1zdMhN4Et8WnrkVQOad6xhUBAJ7Pq4b8A0n96CRE0QdAQ+tJe0/eFiJqIPMuKkyfQ6VncIkGj9VzkByh9uFA==" - payload := "test message" - - db, err := sql.Open("postgres", databaseURL) - if err != nil { - assert.NoError(t, err, "Must be able to connect to challenge-bypass db") - } - - _, err = db.Exec("DELETE from v3_issuer_keys; DELETE FROM v3_issuers; DELETE from redemptions") - assert.NoError(t, err, "Must be able to clear issuers") - - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) - - var config = &aws.Config{ - Region: aws.String("us-west-2"), - Endpoint: aws.String(os.Getenv("DYNAMODB_ENDPOINT")), - } - config.DisableSSL = aws.Bool(true) - svc := dynamodb.New(sess, config) - err = setupDynamodbTables(svc) - assert.NoError(t, err) - - ctx := context.Background() - - client, err := New() - assert.NoError(t, err) - - // If we use cohort 1 we can use the v1 SignCredentials call to mock the signing process. - // This maybe deprecated in the future then we will need to use kafka - issuerRequest := IssuerRequest{ - Name: test.RandomString(), - Cohort: 1, - MaxTokens: test.RandomNonZeroInt(10), - ValidFrom: ptr.FromTime(time.Now()), - ExpiresAt: ptr.FromTime(time.Now().Add(time.Hour)), - Duration: "P1M", - Buffer: test.RandomNonZeroInt(10), - Overlap: test.RandomNonZeroInt(10), - } - - err = client.CreateIssuerV3(context.Background(), issuerRequest) - assert.NoError(t, err) - - issuer, err := client.GetIssuerV3(ctx, issuerRequest.Name) - assert.NoError(t, err) - - assert.Equal(t, issuerRequest.Name, issuer.Name) - assert.Equal(t, issuerRequest.Cohort, issuer.Cohort) - assert.Equal(t, issuerRequest.ExpiresAt.Format(time.RFC3339), issuer.ExpiresAt) - assert.NotEmpty(t, issuer.PublicKey) - - _, err = db.Exec("update v3_issuer_keys set signing_key=$1", sKey) - assert.NoError(t, err) - - resp, err := client.SignCredentials(ctx, issuerRequest.Name, []string{blindedToken}) - assert.NoError(t, err) - assert.Equal(t, resp.SignedTokens[0], signedToken) - - err = client.RedeemCredentialV3(ctx, issuerRequest.Name, preimage, sig, payload) - assert.NoError(t, err) - - _, err = db.Exec("DELETE from redemptions") - assert.NoError(t, err) -} - -// setupDynamodbTables this function sets up tables for use in dynamodb tests. -func setupDynamodbTables(db *dynamodb.DynamoDB) error { - _, _ = db.DeleteTable(&dynamodb.DeleteTableInput{ - TableName: ptr.FromString("redemptions"), - }) - - input := &dynamodb.CreateTableInput{ - TableName: ptr.FromString("redemptions"), - BillingMode: ptr.FromString("PAY_PER_REQUEST"), - AttributeDefinitions: []*dynamodb.AttributeDefinition{ - { - AttributeName: aws.String("id"), - AttributeType: aws.String("S"), - }, - }, - KeySchema: []*dynamodb.KeySchemaElement{ - { - AttributeName: aws.String("id"), - KeyType: aws.String("HASH"), - }, - }, - } - - _, err := db.CreateTable(input) - if err != nil { - return fmt.Errorf("error creating dynamodb table %w", err) - } - - err = tableIsActive(db, *input.TableName, 5*time.Second, 10*time.Millisecond) - if err != nil { - return fmt.Errorf("error table is not active %w", err) - } - - return nil -} - -func tableIsActive(db *dynamodb.DynamoDB, tableName string, timeout, duration time.Duration) error { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - for { - select { - case <-ctx.Done(): - return errors.New("timed out while waiting for table status to become ACTIVE") - case <-time.After(duration): - table, err := db.DescribeTable(&dynamodb.DescribeTableInput{ - TableName: aws.String(tableName), - }) - if err != nil { - return fmt.Errorf("instance.DescribeTable error %w", err) - } - if table.Table == nil || table.Table.TableStatus == nil || *table.Table.TableStatus != "ACTIVE" { - continue - } - return nil - } - } -} diff --git a/libs/clients/cbr/instrumented_client.go b/libs/clients/cbr/instrumented_client.go deleted file mode 100755 index c1ccdf616..000000000 --- a/libs/clients/cbr/instrumented_client.go +++ /dev/null @@ -1,151 +0,0 @@ -package cbr - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "cbr_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// CreateIssuer implements Client -func (_d ClientWithPrometheus) CreateIssuer(ctx context.Context, issuer string, maxTokens int) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateIssuer(ctx, issuer, maxTokens) -} - -// CreateIssuerV3 implements Client -func (_d ClientWithPrometheus) CreateIssuerV3(ctx context.Context, createIssuerV3 IssuerRequest) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateIssuerV3", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateIssuerV3(ctx, createIssuerV3) -} - -// GetIssuer implements Client -func (_d ClientWithPrometheus) GetIssuer(ctx context.Context, issuer string) (ip1 *IssuerResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuer(ctx, issuer) -} - -// GetIssuerV3 implements Client -func (_d ClientWithPrometheus) GetIssuerV3(ctx context.Context, issuer string) (ip1 *IssuerResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuerV3", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuerV3(ctx, issuer) -} - -// RedeemCredential implements Client -func (_d ClientWithPrometheus) RedeemCredential(ctx context.Context, issuer string, preimage string, signature string, payload string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "RedeemCredential", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RedeemCredential(ctx, issuer, preimage, signature, payload) -} - -// RedeemCredentialV3 implements Client -func (_d ClientWithPrometheus) RedeemCredentialV3(ctx context.Context, issuer string, preimage string, signature string, payload string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "RedeemCredentialV3", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RedeemCredentialV3(ctx, issuer, preimage, signature, payload) -} - -// RedeemCredentials implements Client -func (_d ClientWithPrometheus) RedeemCredentials(ctx context.Context, credentials []CredentialRedemption, payload string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "RedeemCredentials", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RedeemCredentials(ctx, credentials, payload) -} - -// SignCredentials implements Client -func (_d ClientWithPrometheus) SignCredentials(ctx context.Context, issuer string, creds []string) (cp1 *CredentialsIssueResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "SignCredentials", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.SignCredentials(ctx, issuer, creds) -} diff --git a/libs/clients/cbr/mock/mock.go b/libs/clients/cbr/mock/mock.go deleted file mode 100644 index b1e80a53c..000000000 --- a/libs/clients/cbr/mock/mock.go +++ /dev/null @@ -1,151 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/cbr/client.go - -// Package mock_cbr is a generated GoMock package. -package mock_cbr - -import ( - context "context" - reflect "reflect" - - cbr "github.com/brave-intl/bat-go/libs/clients/cbr" - gomock "github.com/golang/mock/gomock" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// CreateIssuer mocks base method. -func (m *MockClient) CreateIssuer(ctx context.Context, issuer string, maxTokens int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateIssuer", ctx, issuer, maxTokens) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateIssuer indicates an expected call of CreateIssuer. -func (mr *MockClientMockRecorder) CreateIssuer(ctx, issuer, maxTokens interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateIssuer", reflect.TypeOf((*MockClient)(nil).CreateIssuer), ctx, issuer, maxTokens) -} - -// CreateIssuerV3 mocks base method. -func (m *MockClient) CreateIssuerV3(ctx context.Context, createIssuerV3 cbr.IssuerRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateIssuerV3", ctx, createIssuerV3) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateIssuerV3 indicates an expected call of CreateIssuerV3. -func (mr *MockClientMockRecorder) CreateIssuerV3(ctx, createIssuerV3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateIssuerV3", reflect.TypeOf((*MockClient)(nil).CreateIssuerV3), ctx, createIssuerV3) -} - -// GetIssuer mocks base method. -func (m *MockClient) GetIssuer(ctx context.Context, issuer string) (*cbr.IssuerResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuer", ctx, issuer) - ret0, _ := ret[0].(*cbr.IssuerResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuer indicates an expected call of GetIssuer. -func (mr *MockClientMockRecorder) GetIssuer(ctx, issuer interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuer", reflect.TypeOf((*MockClient)(nil).GetIssuer), ctx, issuer) -} - -// GetIssuerV3 mocks base method. -func (m *MockClient) GetIssuerV3(ctx context.Context, issuer string) (*cbr.IssuerResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuerV3", ctx, issuer) - ret0, _ := ret[0].(*cbr.IssuerResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuerV3 indicates an expected call of GetIssuerV3. -func (mr *MockClientMockRecorder) GetIssuerV3(ctx, issuer interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuerV3", reflect.TypeOf((*MockClient)(nil).GetIssuerV3), ctx, issuer) -} - -// RedeemCredential mocks base method. -func (m *MockClient) RedeemCredential(ctx context.Context, issuer, preimage, signature, payload string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RedeemCredential", ctx, issuer, preimage, signature, payload) - ret0, _ := ret[0].(error) - return ret0 -} - -// RedeemCredential indicates an expected call of RedeemCredential. -func (mr *MockClientMockRecorder) RedeemCredential(ctx, issuer, preimage, signature, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RedeemCredential", reflect.TypeOf((*MockClient)(nil).RedeemCredential), ctx, issuer, preimage, signature, payload) -} - -// RedeemCredentialV3 mocks base method. -func (m *MockClient) RedeemCredentialV3(ctx context.Context, issuer, preimage, signature, payload string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RedeemCredentialV3", ctx, issuer, preimage, signature, payload) - ret0, _ := ret[0].(error) - return ret0 -} - -// RedeemCredentialV3 indicates an expected call of RedeemCredentialV3. -func (mr *MockClientMockRecorder) RedeemCredentialV3(ctx, issuer, preimage, signature, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RedeemCredentialV3", reflect.TypeOf((*MockClient)(nil).RedeemCredentialV3), ctx, issuer, preimage, signature, payload) -} - -// RedeemCredentials mocks base method. -func (m *MockClient) RedeemCredentials(ctx context.Context, credentials []cbr.CredentialRedemption, payload string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RedeemCredentials", ctx, credentials, payload) - ret0, _ := ret[0].(error) - return ret0 -} - -// RedeemCredentials indicates an expected call of RedeemCredentials. -func (mr *MockClientMockRecorder) RedeemCredentials(ctx, credentials, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RedeemCredentials", reflect.TypeOf((*MockClient)(nil).RedeemCredentials), ctx, credentials, payload) -} - -// SignCredentials mocks base method. -func (m *MockClient) SignCredentials(ctx context.Context, issuer string, creds []string) (*cbr.CredentialsIssueResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SignCredentials", ctx, issuer, creds) - ret0, _ := ret[0].(*cbr.CredentialsIssueResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SignCredentials indicates an expected call of SignCredentials. -func (mr *MockClientMockRecorder) SignCredentials(ctx, issuer, creds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignCredentials", reflect.TypeOf((*MockClient)(nil).SignCredentials), ctx, issuer, creds) -} diff --git a/libs/clients/client.go b/libs/clients/client.go deleted file mode 100644 index 39c404784..000000000 --- a/libs/clients/client.go +++ /dev/null @@ -1,333 +0,0 @@ -package clients - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httputil" - "net/url" - "regexp" - "time" - - "github.com/brave-intl/bat-go/libs/closers" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog/log" - "github.com/shopspring/decimal" -) - -// TransferLimit is the limit of BAT any client can transfer -var TransferLimit = decimal.NewFromInt(50) - -// regular expression mapped to the replacement -var redactHeaders = map[*regexp.Regexp][]byte{ - regexp.MustCompile(`(?i)authorization: (?i)basic.+\n`): []byte("Authorization: Basic \n"), - regexp.MustCompile(`(?i)authorization: (?i)bearer.+\n`): []byte("Authorization: Bearer \n"), - regexp.MustCompile(`(?i)x-gemini-apikey: .+\n`): []byte("X-GEMINI-APIKEY: \n"), - regexp.MustCompile(`(?i)signature: .+\n`): []byte("Signature: \n"), - regexp.MustCompile(`(?i)x_cg_pro_api_key=.+\n`): []byte("x_cg_pro_api_key:\n"), - regexp.MustCompile(`(?i)x_cg_pro_api_key=.+&`): []byte("x_cg_pro_api_key:&"), -} - -// RedactSensitiveHeaders from http request dumps -func RedactSensitiveHeaders(corpus []byte) []byte { - for k, v := range redactHeaders { - corpus = k.ReplaceAll(corpus, v) - } - return corpus -} - -var concurrentClientRequests = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "concurrent_client_requests", - Help: "Gauge that holds the current number of client requests", - }, - []string{ - "host", - "method", - }, -) - -func init() { - prometheus.MustRegister(concurrentClientRequests) -} - -// QueryStringBody - a type to generate the query string from a request "body" for the client -type QueryStringBody interface { - // GenerateQueryString - function to generate the query string - GenerateQueryString() (url.Values, error) -} - -// SimpleHTTPClient wraps http.Client for making simple token authorized requests -type SimpleHTTPClient struct { - BaseURL *url.URL - AuthToken string - - client *http.Client -} - -// New returns a new SimpleHTTPClient -func New(serverURL string, authToken string) (*SimpleHTTPClient, error) { - return NewWithHTTPClient(serverURL, authToken, &http.Client{ - Timeout: time.Second * 10, - }) -} - -// NewWithHTTPClient returns a new SimpleHTTPClient, using the provided http.Client -func NewWithHTTPClient(serverURL string, authToken string, client *http.Client) (*SimpleHTTPClient, error) { - baseURL, err := url.Parse(serverURL) - - if err != nil { - return nil, err - } - - return &SimpleHTTPClient{ - BaseURL: baseURL, - AuthToken: authToken, - client: client, - }, nil -} - -// NewWithProxy returns a new SimpleHTTPClient, retrieving the base URL from the environment and adds a proxy -func NewWithProxy(name string, serverURL string, authToken string, proxyURL string) (*SimpleHTTPClient, error) { - baseURL, err := url.Parse(serverURL) - - if err != nil { - return nil, err - } - - var proxy func(*http.Request) (*url.URL, error) - if len(proxyURL) != 0 { - proxiedURL, err := url.Parse(proxyURL) - if err != nil { - panic("HTTP_PROXY is not a valid proxy URL") - } - proxy = http.ProxyURL(proxiedURL) - } else { - proxy = nil - } - return &SimpleHTTPClient{ - BaseURL: baseURL, - AuthToken: authToken, - client: &http.Client{ - Timeout: time.Second * 10, - Transport: middleware.InstrumentRoundTripper( - &http.Transport{ - Proxy: proxy, - }, name), - }, - }, nil -} - -func (c *SimpleHTTPClient) request( - method string, - resolvedURL string, - buf io.Reader, -) (*http.Request, error) { - req, err := http.NewRequest(method, resolvedURL, buf) - if err != nil { - switch err.(type) { - case url.EscapeError: - err = NewHTTPError(err, resolvedURL, ErrUnableToEscapeURL, http.StatusBadRequest, nil) - case url.InvalidHostError: - err = NewHTTPError(err, resolvedURL, ErrInvalidHost, http.StatusBadRequest, nil) - default: - err = NewHTTPError(err, resolvedURL, ErrMalformedRequest, http.StatusBadRequest, nil) - } - return nil, err - } - return req, nil -} - -// newRequest creaates a request, JSON encoding the body passed -func (c *SimpleHTTPClient) newRequest( - ctx context.Context, - method, - path string, - body interface{}, - qsb QueryStringBody, -) (*http.Request, int, error) { - var buf io.ReadWriter - qs := "" - - if qsb != nil { - v, err := qsb.GenerateQueryString() - if err != nil { - // problem generating the query string from the type - return nil, 0, fmt.Errorf("failed to generate query string: %w", err) - } - qs = v.Encode() - } - - resolvedURL := c.BaseURL.ResolveReference(&url.URL{ - Path: path, - RawQuery: qs, - }) - - if body != nil && method != "GET" { - buf = new(bytes.Buffer) - err := json.NewEncoder(buf).Encode(body) - if err != nil { - return nil, 0, errors.Wrap(err, ErrUnableToEncodeBody) - } - } - - req, err := c.request(method, resolvedURL.String(), buf) - if err != nil { - status := 0 - switch err.(type) { - case url.EscapeError: - status = http.StatusBadRequest - err = errors.Wrap(err, ErrUnableToEscapeURL) - case url.InvalidHostError: - status = http.StatusBadRequest - err = errors.Wrap(err, ErrInvalidHost) - } - return nil, status, err - } - - req.Header.Set("accept", "application/json") - if body != nil { - req.Header.Add("content-type", "application/json") - } - requestutils.SetRequestID(ctx, req) - if c.AuthToken != "" { - req.Header.Set("authorization", "Bearer "+c.AuthToken) - } - return req, 0, nil -} - -// NewRequest wraps the new request with a particular error type -func (c *SimpleHTTPClient) NewRequest( - ctx context.Context, - method, - path string, - body interface{}, - qsb QueryStringBody, -) (*http.Request, error) { - req, status, err := c.newRequest(ctx, method, path, body, qsb) - if err != nil { - return nil, NewHTTPError(err, (*req.URL).String(), "request", status, body) - } - return req, err -} - -// Do the specified http request, decoding the JSON result into v -func (c *SimpleHTTPClient) do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) { - - // concurrent client request instrumentation - concurrentClientRequests.With( - prometheus.Labels{ - "host": req.URL.Host, "method": req.Method, - }).Inc() - - defer func() { - concurrentClientRequests.With( - prometheus.Labels{ - "host": req.URL.Host, "method": req.Method, - }).Dec() - }() - - logger := log.Ctx(ctx) - debug, okDebug := ctx.Value(appctx.DebugLoggingCTXKey).(bool) - - if okDebug && debug { - // if debug is set, then dump response - // dump out the full request, right before we submit it - requestDump, err := httputil.DumpRequestOut(req, true) - if err != nil { - logger.Error().Err(err).Str("type", "http.Request").Msg("failed to dump request body") - } else { - logger.Debug().Str("type", "http.Request").Msg(string(RedactSensitiveHeaders(requestDump))) - } - } - - if c.client.Timeout > 0 { - // put a timeout on the request context - reqCtx, cancel := context.WithTimeout(context.Background(), c.client.Timeout) - scopedCtx := appctx.Wrap(req.Context(), reqCtx) - // cancel the context when complete - defer cancel() - - req = req.WithContext(scopedCtx) - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - status := resp.StatusCode - defer closers.Panic(ctx, resp.Body) - - if okDebug && debug { - // if debug is set, then dump response - dump, err := httputil.DumpResponse(resp, true) - if err != nil { - logger.Error().Err(err).Str("type", "http.Response").Msg("failed to dump response body") - } else { - logger.Debug().Str("type", "http.Response").Msg(string(dump)) - } - } - - // helpful if you want to read the body as it is - bodyBytes, _ := requestutils.Read(ctx, resp.Body) - _ = resp.Body.Close() // must close - resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) - - if status >= 200 && status <= 299 { - if v != nil { - err = json.Unmarshal(bodyBytes, v) - if err != nil { - return resp, errors.Wrap(err, ErrUnableToDecode) - } - } - - return resp, nil - } - - logger.Warn(). - Int("response_status", status). - Err(err). - Str("body", string(bodyBytes)). // add errored body into the messaging - Msg("failed http client call") - logger.Debug().Str("host", req.URL.Host).Str("path", req.URL.Path).Str("body", string(bodyBytes)).Msg("failed http client call") - return resp, errors.Wrap(err, ErrProtocolError) -} - -// RespErrData - error data for http response -type RespErrData struct { - ResponseHeaders interface{} - Body interface{} -} - -// Do the specified http request, decoding the JSON result into v -func (c *SimpleHTTPClient) Do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) { - resp, err := c.do(ctx, req, v) - if err != nil { - // errors returned from c.do could be go errors or upstream api errors - if resp != nil { - // if there was an error from the service, read the response body - // and inject into error for later - b, _ := ioutil.ReadAll(resp.Body) - rb := string(b) - resp.Body = ioutil.NopCloser(bytes.NewBuffer(b)) - - // put response body/headers in the err state data - errorData := RespErrData{ - ResponseHeaders: resp.Header, - Body: rb, - } - - return resp, NewHTTPError(err, req.URL.String(), "response", resp.StatusCode, errorData) - } - return nil, fmt.Errorf("failed c.do, no response body: %w", err) - } - return resp, nil -} diff --git a/libs/clients/client_test.go b/libs/clients/client_test.go deleted file mode 100644 index 4071ea2b6..000000000 --- a/libs/clients/client_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package clients - -import ( - "context" - "fmt" - "github.com/brave-intl/bat-go/libs/errors" - testutils "github.com/brave-intl/bat-go/libs/test" - "github.com/stretchr/testify/assert" - "net/http" - "net/http/httptest" - "testing" -) - -func TestDo_ErrorWithResponse(t *testing.T) { - errorMsg := testutils.RandomString() - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - _, err := w.Write([]byte(errorMsg)) - assert.NoError(t, err) - })) - defer ts.Close() - - req, err := http.NewRequest(http.MethodGet, ts.URL, nil) - assert.NoError(t, err) - - client, err := New(ts.URL, "") - assert.NoError(t, err) - - // pass data as invalid result type to cause error - var data *string - response, err := client.Do(context.Background(), req, data) - - assert.IsType(t, &errors.ErrorBundle{}, err) - assert.NotNil(t, response) - - actual := err.(*errors.ErrorBundle) - assert.Equal(t, "response", actual.Error()) - assert.NotNil(t, actual.Cause(), ErrUnableToDecode) - - httpState := actual.Data().(HTTPState) - assert.Equal(t, httpState.Status, http.StatusOK) - assert.Equal(t, ts.URL, httpState.Path) - assert.Contains(t, fmt.Sprintf("+%v", httpState.Body), errorMsg) -} diff --git a/libs/clients/coingecko/client.go b/libs/clients/coingecko/client.go deleted file mode 100644 index 1d928bae3..000000000 --- a/libs/clients/coingecko/client.go +++ /dev/null @@ -1,435 +0,0 @@ -package coingecko - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/closers" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/gomodule/redigo/redis" - "github.com/google/go-querystring/query" - "github.com/shopspring/decimal" -) - -const ( - // CoinMarketsCacheTTLSeconds is how long FetchCoinMarkets responses cached - // in Redis are considered valid - CoinMarketsCacheTTLSeconds = 60 * 60 - coinMarketsPageSize = 250 - coingeckoImageProxy = "assets.cgproxy.brave.com" -) - -// Client abstracts over the underlying client -type Client interface { - FetchSimplePrice(ctx context.Context, ids string, vsCurrencies string, include24hrChange bool) (*SimplePriceResponse, error) - FetchCoinList(ctx context.Context, includePlatform bool) (*CoinListResponse, error) - FetchSupportedVsCurrencies(ctx context.Context) (*SupportedVsCurrenciesResponse, error) - FetchMarketChart(ctx context.Context, id string, vsCurrency string, days float32, cacheDurationSeconds int) (*MarketChartResponse, time.Time, error) - FetchCoinMarkets(ctx context.Context, vsCurrency string, limit int) (*CoinMarketResponse, time.Time, error) -} - -// HTTPClient wraps http.Client for interacting with the coingecko server -type HTTPClient struct { - baseParams - client *clients.SimpleHTTPClient - redis *redis.Pool -} - -// NewWithContext returns a new HTTPClient, retrieving the base URL from the context -func NewWithContext(ctx context.Context, redis *redis.Pool) (Client, error) { - // get the server url from context - serverURL, err := appctx.GetStringFromContext(ctx, appctx.CoingeckoServerCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get CoingeckoServer from context: %w", err) - } - - // get the server access token from context - accessToken, err := appctx.GetStringFromContext(ctx, appctx.CoingeckoAccessTokenCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get CoingeckoAccessToken from context: %w", err) - } - - client, err := clients.NewWithHTTPClient(serverURL, "", &http.Client{ - Timeout: time.Second * 30, - }) - if err != nil { - return nil, err - } - - return NewClientWithPrometheus( - &HTTPClient{ - baseParams: baseParams{ - APIKey: accessToken, - }, - client: client, - redis: redis, - }, "coingecko_context_client"), nil -} - -func (c *HTTPClient) cacheKey(ctx context.Context, path string, body clients.QueryStringBody) (string, error) { - qs, err := body.GenerateQueryString() - if err != nil { - return "", err - } - - // redact API key - qs.Del("x_cg_pro_api_key") - - return c.client.BaseURL.ResolveReference(&url.URL{ - Path: path, - RawQuery: qs.Encode(), - }).String(), nil -} - -type cacheEntry struct { - Payload string `json:"payload"` - LastUpdated time.Time `json:"lastUpdated"` -} - -// baseParams that must be included with every request -type baseParams struct { - APIKey string `url:"x_cg_pro_api_key"` -} - -// simplePriceParams for fetching prices -type simplePriceParams struct { - baseParams - Ids string `url:"ids"` - VsCurrencies string `url:"vs_currencies"` - Include24hrChange bool `url:"include_24hr_change,omitempty"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (p *simplePriceParams) GenerateQueryString() (url.Values, error) { - return query.Values(p) -} - -// SimplePriceResponse is the response received from coingecko -type SimplePriceResponse map[string]map[string]decimal.Decimal - -// FetchSimplePrice fetches the rate of a currency to BAT -func (c *HTTPClient) FetchSimplePrice(ctx context.Context, ids string, vsCurrencies string, include24hrChange bool) (*SimplePriceResponse, error) { - req, err := c.client.NewRequest(ctx, "GET", "/api/v3/simple/price", nil, &simplePriceParams{ - baseParams: c.baseParams, - Ids: ids, - VsCurrencies: vsCurrencies, - Include24hrChange: include24hrChange, - }) - if err != nil { - return nil, err - } - - var body SimplePriceResponse - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - - return &body, nil -} - -// marketChartParams for fetching market chart -type marketChartParams struct { - baseParams - ID string `url:"id"` - VsCurrency string `url:"vs_currency"` - Days float32 `url:"days,omitempty"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (p *marketChartParams) GenerateQueryString() (url.Values, error) { - return query.Values(p) -} - -// MarketChartResponse is the response received from coingecko -type MarketChartResponse struct { - Prices [][]decimal.Decimal `json:"prices"` - MarketCaps [][]decimal.Decimal `json:"market_caps"` - TotalVolumes [][]decimal.Decimal `json:"total_volumes"` -} - -// FetchMarketChart fetches the history rate of a currency -func (c *HTTPClient) FetchMarketChart( - ctx context.Context, - id string, vsCurrency string, - days float32, - cacheDurationSeconds int, -) (*MarketChartResponse, time.Time, error) { - updated := time.Now() - - url := fmt.Sprintf("/api/v3/coins/%s/market_chart", id) - params := &marketChartParams{ - baseParams: c.baseParams, - ID: id, - VsCurrency: vsCurrency, - Days: days, - } - cacheKey, err := c.cacheKey(ctx, url, params) - if err != nil { - return nil, updated, err - } - - conn := c.redis.Get() - defer closers.Log(ctx, conn) - - var body MarketChartResponse - var entry cacheEntry - entryBytes, err := redis.Bytes(conn.Do("GET", cacheKey)) - if err == nil { - err = json.Unmarshal(entryBytes, &entry) - if err != nil { - return nil, updated, err - } - err = json.Unmarshal([]byte(entry.Payload), &body) - if err != nil { - return nil, updated, err - } - - // 1h chart is cached for 2.5m - // 1d chart is cached for 1 hour - // 1w chart is cached for 7 hours - // etc - secondsSinceUpdate := int(time.Since(entry.LastUpdated).Seconds()) - if secondsSinceUpdate < cacheDurationSeconds { - return &body, entry.LastUpdated, err - } - } - - req, err := c.client.NewRequest(ctx, "GET", url, nil, params) - if err != nil { - return nil, updated, err - } - - _, err = c.client.Do(ctx, req, &body) - if err != nil { - // attempt to use cache response on error if exists - if len(entry.Payload) > 0 { - return &body, entry.LastUpdated, nil - } - return nil, updated, err - } - - bodyBytes, err := json.Marshal(&body) - if err != nil { - return nil, updated, err - } - entryBytes, err = json.Marshal(&cacheEntry{Payload: string(bodyBytes), LastUpdated: updated}) - if err != nil { - return nil, updated, err - } - _, err = conn.Do("SET", cacheKey, entryBytes) - if err != nil { - return nil, updated, err - } - - return &body, updated, nil -} - -type coinListParams struct { - baseParams - IncludePlatform bool `url:"include_platform"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (p *coinListParams) GenerateQueryString() (url.Values, error) { - return query.Values(p) -} - -// CoinInfoPlatform - platform coin info -type CoinInfoPlatform struct { - Ethereum string `json:"ethereum,omitempty"` -} - -// CoinInfo - info about coin -type CoinInfo struct { - ID string `json:"id"` - Symbol string `json:"symbol"` - Name string `json:"name"` - Platforms CoinInfoPlatform `json:"platforms,omitempty"` -} - -// CoinListResponse is the response received from coingecko -type CoinListResponse []CoinInfo - -// FetchCoinList fetches the list of supported coins -func (c *HTTPClient) FetchCoinList(ctx context.Context, includePlatform bool) (*CoinListResponse, error) { - req, err := c.client.NewRequest(ctx, "GET", "/api/v3/coins/list", nil, &coinListParams{ - baseParams: c.baseParams, - IncludePlatform: includePlatform, - }) - if err != nil { - return nil, err - } - - var body CoinListResponse - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - - return &body, nil -} - -type supportedVsCurrenciesParams struct { - baseParams -} - -// GenerateQueryString - implement the QueryStringBody interface -func (p *supportedVsCurrenciesParams) GenerateQueryString() (url.Values, error) { - return query.Values(p) -} - -// SupportedVsCurrenciesResponse is the response received from coingecko -type SupportedVsCurrenciesResponse []string - -// FetchSupportedVsCurrencies fetches the list of supported vs currencies -func (c *HTTPClient) FetchSupportedVsCurrencies(ctx context.Context) (*SupportedVsCurrenciesResponse, error) { - req, err := c.client.NewRequest(ctx, "GET", "/api/v3/simple/supported_vs_currencies", nil, &supportedVsCurrenciesParams{ - baseParams: c.baseParams, - }) - if err != nil { - return nil, err - } - - var body SupportedVsCurrenciesResponse - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - - return &body, nil -} - -type coinMarketParams struct { - baseParams - VsCurrency string `url:"vs_currency"` - Page int `url:"page"` - PerPage int `url:"per_page"` - Limit int `url:"-"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (p *coinMarketParams) GenerateQueryString() (url.Values, error) { - p.Page = 1 - p.PerPage = coinMarketsPageSize - return query.Values(p) -} - -// CoinMarket represents the market data for a single coin returned in -// FetchCoinMarkets call to coingecko -type CoinMarket struct { - ID string `json:"id"` - Symbol string `json:"symbol"` - Name string `json:"name"` - Image string `json:"image"` - MarketCap int `json:"market_cap"` - MarketCapRank int `json:"market_cap_rank"` - CurrentPrice float64 `json:"current_price"` - PriceChange24h float64 `json:"price_change_24h"` - PriceChangePercentage24h float64 `json:"price_change_percentage_24h"` - TotalVolume float64 `json:"total_volume"` -} - -// CoinMarketResponse is the coingecko response for FetchCoinMarkets -type CoinMarketResponse []*CoinMarket - -func (cmr *CoinMarketResponse) applyLimit(limit int) CoinMarketResponse { - return (*cmr)[:limit] -} - -// FetchCoinMarkets fetches the market data for the top coins -func (c *HTTPClient) FetchCoinMarkets( - ctx context.Context, - vsCurrency string, - limit int, -) (*CoinMarketResponse, time.Time, error) { - updated := time.Now() - cgURL := "/api/v3/coins/markets" - params := &coinMarketParams{ - baseParams: c.baseParams, - VsCurrency: vsCurrency, - Limit: limit, - } - - cacheKey, err := c.cacheKey(ctx, cgURL, params) - if err != nil { - return nil, updated, err - } - - conn := c.redis.Get() - defer closers.Log(ctx, conn) - - var body CoinMarketResponse - var entry cacheEntry - - // Check cache first before making request to Coingecko - entryBytes, err := redis.Bytes(conn.Do("GET", cacheKey)) - if err == nil { - err = json.Unmarshal(entryBytes, &entry) - if err != nil { - return nil, updated, err - } - - err = json.Unmarshal([]byte(entry.Payload), &body) - if err != nil { - return nil, updated, err - } - - // Check if cache is still fresh - if time.Since(entry.LastUpdated).Seconds() < float64(CoinMarketsCacheTTLSeconds) { - body = (&body).applyLimit(params.Limit) - return &body, entry.LastUpdated, err - } - } - - req, err := c.client.NewRequest(ctx, "GET", cgURL, nil, params) - if err != nil { - return nil, updated, err - } - - _, err = c.client.Do(ctx, req, &body) - if err != nil { - // attempt to use cache response on error if exists - if len(entry.Payload) > 0 { - body = (&body).applyLimit(params.Limit) - return &body, entry.LastUpdated, nil - } - - return nil, updated, err - } - - // Replace image URL with our own proxy - for _, market := range body { - imageURL, err := url.Parse(market.Image) - if err != nil { - return nil, updated, err - } - imageURL.Host = coingeckoImageProxy - market.Image = imageURL.String() - } - - bodyBytes, err := json.Marshal(&body) - if err != nil { - return nil, updated, err - } - - // Update the cache - entryBytes, err = json.Marshal(&cacheEntry{Payload: string(bodyBytes), LastUpdated: updated}) - if err != nil { - return nil, updated, err - } - - _, err = conn.Do("SET", cacheKey, entryBytes) - if err != nil { - return nil, updated, err - } - - // Apply the limit only after caching the all the results - body = (&body).applyLimit(params.Limit) - return &body, updated, nil -} diff --git a/libs/clients/coingecko/client_test.go b/libs/clients/coingecko/client_test.go deleted file mode 100644 index b26ae132e..000000000 --- a/libs/clients/coingecko/client_test.go +++ /dev/null @@ -1,159 +0,0 @@ -//go:build integration && vpn -// +build integration,vpn - -package coingecko_test - -import ( - "context" - "encoding/json" - "net/url" - "os" - "testing" - - "github.com/brave-intl/bat-go/libs/clients/coingecko" - appctx "github.com/brave-intl/bat-go/libs/context" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/gomodule/redigo/redis" - "github.com/stretchr/testify/suite" -) - -type CoingeckoTestSuite struct { - suite.Suite - redisPool *redis.Pool - client coingecko.Client - ctx context.Context -} - -func TestCoingeckoTestSuite(t *testing.T) { - suite.Run(t, new(CoingeckoTestSuite)) -} - -var ( - coingeckoService string = "https://api.coingecko.com/" - coingeckoToken string - coingeckoCoinLimit int = 2 - coingeckoCurrencyLimit int = 2 -) - -func (suite *CoingeckoTestSuite) SetupTest() { - // setup the context - suite.ctx = context.Background() - - // setup debug for client - suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, false) - // setup debug log level - suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "info") - - // setup a logger and put on context - suite.ctx, _ = logutils.SetupLogger(suite.ctx) - - // setup server location - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoServerCTXKey, coingeckoService) - // setup token (using public api for tests) - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoAccessTokenCTXKey, coingeckoToken) - // coin limit - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoCoinLimitCTXKey, coingeckoCoinLimit) - // vs-currency limit - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoVsCurrencyLimitCTXKey, coingeckoCurrencyLimit) - - var redisAddr string = "redis://grant-redis" - if len(os.Getenv("REDIS_ADDR")) > 0 { - redisAddr = os.Getenv("REDIS_ADDR") - } - - suite.redisPool = &redis.Pool{ - MaxIdle: 50, - MaxActive: 1000, - Dial: func() (redis.Conn, error) { - conn, err := redis.DialURL(redisAddr) - suite.Require().NoError(err, "failed to connect to redis") - return conn, err - }, - } - - rConn := suite.redisPool.Get() - defer rConn.Close() - s, err := redis.String(rConn.Do("PING")) - suite.Require().NoError(err, "failed to connect to redis") - suite.Require().True(s == "PONG", "bad response from redis") - - // setup the client under test, no redis, will test redis interactions in ratios service - suite.client, err = coingecko.NewWithContext(suite.ctx, suite.redisPool) - suite.Require().NoError(err, "Must be able to correctly initialize the client") - -} - -func (suite *CoingeckoTestSuite) TestFetchSimplePrice() { - resp, err := suite.client.FetchSimplePrice(suite.ctx, "basic-attention-token", "usd", false) - suite.Require().NoError(err, "should be able to fetch the simple price") - - // simple price response should have a bat key - _, ok := (*resp)["basic-attention-token"] - suite.Require().True(ok, "should have bat in the response") -} - -func (suite *CoingeckoTestSuite) TestFetchCoinList() { - resp, err := suite.client.FetchCoinList(suite.ctx, false) - suite.Require().NoError(err, "should be able to fetch the coin list") - - // simple price response should have a bat key - var foundBAT bool - for _, v := range *resp { - if v.ID == "basic-attention-token" { - foundBAT = true - break - } - } - suite.Require().True(foundBAT, "should have bat in the response") -} - -func (suite *CoingeckoTestSuite) TestFetchSupportedVsCurrencies() { - resp, err := suite.client.FetchSupportedVsCurrencies(suite.ctx) - suite.Require().NoError(err, "should be able to fetch the vs_currency list") - - // simple price response should have a bat key - var foundUSD bool - for _, v := range *resp { - if v == "usd" { - foundUSD = true - break - } - } - suite.Require().True(foundUSD, "should have usd in the response") -} - -func (suite *CoingeckoTestSuite) TestFetchMarketChart() { - resp, t, err := suite.client.FetchMarketChart(suite.ctx, "basic-attention-token", "usd", 1.0, 3600) - suite.Require().NoError(err, "should be able to fetch the market chart") - - // call again, make sure you get back the same resp and t - resp1, t1, err := suite.client.FetchMarketChart(suite.ctx, "basic-attention-token", "usd", 1.0, 3600) - suite.Require().NoError(err, "should be able to fetch the market chart") - - b, err := json.Marshal(resp) - suite.Require().NoError(err, "should marshal first resp") - - b1, err := json.Marshal(resp1) - suite.Require().NoError(err, "should marshal second resp") - - suite.Require().True(string(b) == string(b1) && t1.Equal(t), "didn't use cached response") - -} - -func (suite *CoingeckoTestSuite) TestFetchCoinMarkets() { - resp, t, err := suite.client.FetchCoinMarkets(suite.ctx, "usd", 1) - suite.Require().NoError(err, "should be able to fetch the coin markets") - suite.Require().Equal(1, len(*resp), "should have a response length of 1 for limit=1") - suite.Require().NotNil((*resp)[0].CurrentPrice, "should have a value for price") - suite.Require().NotEqual((*resp)[0].CurrentPrice, 0, "bitcoin is never going to 0") - - // call again but with biggger limit - // in this case we should have more results, but only used cached response from redis - resp1, t1, err := suite.client.FetchCoinMarkets(suite.ctx, "usd", 10) - suite.Require().NoError(err, "should be able to fetch the coin markets") - suite.Require().Equal(10, len(*resp1), "should have a response length of 10 for limit=10") - suite.Require().Equal(t.Unix(), t1.Unix(), "the lastUpdated time should be equal because of cache usage") - u, err := url.Parse((*resp1)[0].Image) - suite.Require().NoError(err) - suite.Require().Equal(u.Host, "assets.cgproxy.brave.com", "image host should be the brave proxy") -} diff --git a/libs/clients/coingecko/instrumented_client.go b/libs/clients/coingecko/instrumented_client.go deleted file mode 100755 index 79ed58365..000000000 --- a/libs/clients/coingecko/instrumented_client.go +++ /dev/null @@ -1,109 +0,0 @@ -package coingecko - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "coingecko_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// FetchCoinList implements Client -func (_d ClientWithPrometheus) FetchCoinList(ctx context.Context, includePlatform bool) (cp1 *CoinListResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchCoinList", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchCoinList(ctx, includePlatform) -} - -// FetchCoinMarkets implements Client -func (_d ClientWithPrometheus) FetchCoinMarkets(ctx context.Context, vsCurrency string, limit int) (cp1 *CoinMarketResponse, t1 time.Time, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchCoinMarkets", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchCoinMarkets(ctx, vsCurrency, limit) -} - -// FetchMarketChart implements Client -func (_d ClientWithPrometheus) FetchMarketChart(ctx context.Context, id string, vsCurrency string, days float32, cacheDurationSeconds int) (mp1 *MarketChartResponse, t1 time.Time, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchMarketChart", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchMarketChart(ctx, id, vsCurrency, days, cacheDurationSeconds) -} - -// FetchSimplePrice implements Client -func (_d ClientWithPrometheus) FetchSimplePrice(ctx context.Context, ids string, vsCurrencies string, include24hrChange bool) (sp1 *SimplePriceResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchSimplePrice", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchSimplePrice(ctx, ids, vsCurrencies, include24hrChange) -} - -// FetchSupportedVsCurrencies implements Client -func (_d ClientWithPrometheus) FetchSupportedVsCurrencies(ctx context.Context) (sp1 *SupportedVsCurrenciesResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchSupportedVsCurrencies", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchSupportedVsCurrencies(ctx) -} diff --git a/libs/clients/coingecko/mock/mock.go b/libs/clients/coingecko/mock/mock.go deleted file mode 100644 index 756b960c9..000000000 --- a/libs/clients/coingecko/mock/mock.go +++ /dev/null @@ -1,114 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/coingecko/client.go - -// Package mock_coingecko is a generated GoMock package. -package mock_coingecko - -import ( - context "context" - reflect "reflect" - time "time" - - coingecko "github.com/brave-intl/bat-go/libs/clients/coingecko" - gomock "github.com/golang/mock/gomock" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// FetchCoinList mocks base method. -func (m *MockClient) FetchCoinList(ctx context.Context, includePlatform bool) (*coingecko.CoinListResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchCoinList", ctx, includePlatform) - ret0, _ := ret[0].(*coingecko.CoinListResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchCoinList indicates an expected call of FetchCoinList. -func (mr *MockClientMockRecorder) FetchCoinList(ctx, includePlatform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchCoinList", reflect.TypeOf((*MockClient)(nil).FetchCoinList), ctx, includePlatform) -} - -// FetchCoinMarkets mocks base method. -func (m *MockClient) FetchCoinMarkets(ctx context.Context, vsCurrency string, limit int) (*coingecko.CoinMarketResponse, time.Time, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchCoinMarkets", ctx, vsCurrency, limit) - ret0, _ := ret[0].(*coingecko.CoinMarketResponse) - ret1, _ := ret[1].(time.Time) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FetchCoinMarkets indicates an expected call of FetchCoinMarkets. -func (mr *MockClientMockRecorder) FetchCoinMarkets(ctx, vsCurrency, limit interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchCoinMarkets", reflect.TypeOf((*MockClient)(nil).FetchCoinMarkets), ctx, vsCurrency, limit) -} - -// FetchMarketChart mocks base method. -func (m *MockClient) FetchMarketChart(ctx context.Context, id, vsCurrency string, days float32, cacheDurationSeconds int) (*coingecko.MarketChartResponse, time.Time, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchMarketChart", ctx, id, vsCurrency, days, cacheDurationSeconds) - ret0, _ := ret[0].(*coingecko.MarketChartResponse) - ret1, _ := ret[1].(time.Time) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FetchMarketChart indicates an expected call of FetchMarketChart. -func (mr *MockClientMockRecorder) FetchMarketChart(ctx, id, vsCurrency, days, cacheDurationSeconds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMarketChart", reflect.TypeOf((*MockClient)(nil).FetchMarketChart), ctx, id, vsCurrency, days, cacheDurationSeconds) -} - -// FetchSimplePrice mocks base method. -func (m *MockClient) FetchSimplePrice(ctx context.Context, ids, vsCurrencies string, include24hrChange bool) (*coingecko.SimplePriceResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchSimplePrice", ctx, ids, vsCurrencies, include24hrChange) - ret0, _ := ret[0].(*coingecko.SimplePriceResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchSimplePrice indicates an expected call of FetchSimplePrice. -func (mr *MockClientMockRecorder) FetchSimplePrice(ctx, ids, vsCurrencies, include24hrChange interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchSimplePrice", reflect.TypeOf((*MockClient)(nil).FetchSimplePrice), ctx, ids, vsCurrencies, include24hrChange) -} - -// FetchSupportedVsCurrencies mocks base method. -func (m *MockClient) FetchSupportedVsCurrencies(ctx context.Context) (*coingecko.SupportedVsCurrenciesResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchSupportedVsCurrencies", ctx) - ret0, _ := ret[0].(*coingecko.SupportedVsCurrenciesResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchSupportedVsCurrencies indicates an expected call of FetchSupportedVsCurrencies. -func (mr *MockClientMockRecorder) FetchSupportedVsCurrencies(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchSupportedVsCurrencies", reflect.TypeOf((*MockClient)(nil).FetchSupportedVsCurrencies), ctx) -} diff --git a/libs/clients/errors.go b/libs/clients/errors.go deleted file mode 100644 index da781d107..000000000 --- a/libs/clients/errors.go +++ /dev/null @@ -1,55 +0,0 @@ -package clients - -import ( - "errors" - "fmt" - - errorutils "github.com/brave-intl/bat-go/libs/errors" -) - -var ( - // ErrTransferExceedsLimit - ErrTransferExceedsLimit = errors.New("transfer exceeds limit") - // ErrUnableToDecode unable to decode body - ErrUnableToDecode = "unable to decode response" - // ErrProtocolError the error was within the data that went into the endpoint - ErrProtocolError = "protocol error" - // ErrUnableToEscapeURL the url could nto be escaped - ErrUnableToEscapeURL = "unable to escape url" - // ErrInvalidHost the host was invalid - ErrInvalidHost = "invalid host" - // ErrMalformedRequest the request was malformed - ErrMalformedRequest = "malformed request" - // ErrUnableToEncodeBody body could not be decoded - ErrUnableToEncodeBody = "unable to encode body" -) - -// HTTPState captures the state of the response to be read by lower fns in the stack -type HTTPState struct { - Status int - Path string - Body interface{} -} - -// NewHTTPError creates a new errors.ErrorBundle with an HTTPState wrapping the status, path and v. -func NewHTTPError(err error, path, message string, status int, v interface{}) error { - return errorutils.New(err, message, HTTPState{ - Status: status, - Path: path, - Body: v, - }) -} - -// Error returns the error string -func (bfe *BitflyerError) Error() string { - return fmt.Sprintf("message: %s - label: %s - status: %d - ids: %v - http status: %d", bfe.Message, bfe.Label, bfe.Status, bfe.ErrorIDs, bfe.HTTPStatusCode) -} - -// BitflyerError holds error info directly from bitflyer -type BitflyerError struct { - Message string `json:"message"` - ErrorIDs []string `json:"errors"` - Label string `json:"label"` - Status int `json:"status"` // might be signed - HTTPStatusCode int `json:"-"` -} diff --git a/libs/clients/gemini/client.go b/libs/clients/gemini/client.go deleted file mode 100644 index c72005303..000000000 --- a/libs/clients/gemini/client.go +++ /dev/null @@ -1,478 +0,0 @@ -package gemini - -import ( - "context" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/url" - "os" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/custodian" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/google/go-querystring/query" - "github.com/shengdoushi/base58" - "github.com/shopspring/decimal" -) - -// PrivateRequestSequence handles the ability to sign a request multiple times -type PrivateRequestSequence struct { - // the baseline object, corresponds to the signature in the first item - // must update the nonce before sending otherwise invalid signature will be encountered - Base BulkPayoutPayload `json:"base"` - Signatures []string `json:"signatures"` // a list of hex encoded singatures - APIKey string `json:"apikey"` // the api key that corresponds to the checksum server side - Account *string `json:"account,omitempty"` -} - -// PayoutPayload contains details about transactions to be confirmed -type PayoutPayload struct { - TxRef string `json:"tx_ref"` - Amount decimal.Decimal `json:"amount"` - Currency string `json:"currency"` - Destination string `json:"destination"` - Account *string `json:"account,omitempty"` -} - -// CheckTxPayload get the tx status payload structure -type CheckTxPayload struct { - Request string `json:"request"` - Nonce int64 `json:"nonce"` -} - -// AccountListPayload retrieves all accounts associated with a gemini key -type AccountListPayload struct { - Request string `json:"request"` - Nonce int64 `json:"nonce"` -} - -// BalancesPayload retrieves all accounts associated with a gemini key -type BalancesPayload struct { - Request string `json:"request"` - Nonce int64 `json:"nonce"` - Account *string `json:"account,omitempty"` -} - -// BulkPayoutPayload the payload to be base64'd -type BulkPayoutPayload struct { - Request string `json:"request"` - Nonce int64 `json:"nonce"` - Payouts []PayoutPayload `json:"payouts"` - OauthClientID string `json:"client_id"` - Account *string `json:"account,omitempty"` -} - -func nonce() int64 { - return time.Now().UTC().UnixNano() -} - -// SettlementTransactionToPayoutPayload converts to a payout request -func SettlementTransactionToPayoutPayload(tx *custodian.Transaction) PayoutPayload { - currency := "BAT" - if tx.Currency != "" { - currency = tx.Currency - } - return PayoutPayload{ - TxRef: GenerateTxRef(tx), - Amount: tx.Amount, - Currency: currency, - Destination: tx.Destination, - } -} - -// GenerateTxRef generates a deterministic transaction reference id for idempotency -func GenerateTxRef(tx *custodian.Transaction) string { - key := strings.Join([]string{ - tx.SettlementID, - // if you have to resubmit referrals to get status - tx.Type, - tx.Destination, - tx.Channel, - }, "_") - bytes := sha256.Sum256([]byte(key)) - refID := base58.Encode(bytes[:], base58.IPFSAlphabet) - return refID -} - -// NewBulkPayoutPayload generate a new bulk payout payload -func NewBulkPayoutPayload(account *string, oauthClientID string, payouts *[]PayoutPayload) BulkPayoutPayload { - if oauthClientID == "" { - panic("unable to sign a payload without an oauth client id (GEMINI_CLIENT_ID)") - } - return BulkPayoutPayload{ - Account: account, - OauthClientID: oauthClientID, - Request: "/v1/payments/bulkPay", - Nonce: nonce(), - Payouts: *payouts, - } -} - -// NewCheckTxPayload generate a new payload for the check tx api -func NewCheckTxPayload(url string) CheckTxPayload { - return CheckTxPayload{ - Request: url, - Nonce: nonce(), - } -} - -// NewAccountListPayload generate a new account list payload -func NewAccountListPayload() AccountListPayload { - return AccountListPayload{ - Request: "/v1/account/list", - Nonce: nonce(), - } -} - -// NewBalancesPayload generate a new account list payload -func NewBalancesPayload(account *string) BalancesPayload { - return BalancesPayload{ - Request: "/v1/balances", - Nonce: nonce(), - Account: account, - } -} - -// PayoutResult contains details about a newly created or fetched issuer -type PayoutResult struct { - Result string `json:"result"` // OK or Error - TxRef string `json:"tx_ref"` - Amount *decimal.Decimal `json:"amount"` - Currency *string `json:"currency"` - Destination *string `json:"destination"` - Status *string `json:"status"` - Reason *string `json:"reason"` -} - -// Balance holds balance info -type Balance struct { - Type string `json:"type"` - Currency string `json:"currency"` - Amount decimal.Decimal `json:"amount"` - Available decimal.Decimal `json:"available"` - AvailableForWithdrawal decimal.Decimal `json:"availableForWithdrawal"` -} - -// Account holds account info -type Account struct { - Name string `json:"name"` - Class string `json:"account"` - Type string `json:"type"` - CounterpartyID string `json:"counterparty_id"` - CreatedAt int64 `json:"created"` -} - -// GenerateLog creates a log -func (pr PayoutResult) GenerateLog() string { - if pr.Result == "OK" { - return "" - } - return strings.Join([]string{pr.Result, pr.TxRef, *pr.Status, *pr.Reason}, ": ") -} - -// Client abstracts over the underlying client -type Client interface { - // FetchValidatedAccount given a verificationToken validate the token is authentic and get the unique account id - FetchValidatedAccount(ctx context.Context, verificationToken, recipientID string) (ValidatedAccount, error) - // FetchAccountList requests account information to scope future requests - FetchAccountList(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]Account, error) - // FetchBalances requests balance information for a given account - FetchBalances(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]Balance, error) - // UploadBulkPayout posts a signed bulk layout to gemini - UploadBulkPayout(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]PayoutResult, error) - // CheckTxStatus checks the status of a transaction - CheckTxStatus(ctx context.Context, APIKEY string, clientID string, txRef string) (*PayoutResult, error) -} - -// HTTPClient wraps http.Client for interacting with the cbr server -type HTTPClient struct { - client *clients.SimpleHTTPClient -} - -// Conf some common gemini configuration values -type Conf struct { - ClientID string - APIKey string - Secret string - SettlementAddress string -} - -var ( - // ErrInvalidServerURL - invalid server url error - ErrInvalidServerURL = errors.New("invalid gemini server url") - // ErrInvalidToken - invalid token error - ErrInvalidToken = errors.New("invalid gemini token") -) - -// NewWithContext returns a new HTTPClient, retrieving the base URL from the context -func NewWithContext(ctx context.Context) (Client, error) { - // get the server url - serverURL, ok := ctx.Value(appctx.GeminiServerURLCTXKey).(string) - if !ok || len(serverURL) == 0 { - return nil, ErrInvalidServerURL - } - - // get the proxy url if applicable - proxyURL, ok := ctx.Value(appctx.GeminiProxyURLCTXKey).(string) - if !ok || len(proxyURL) == 0 { - proxyURL = "" - } - - // get the token - token, ok := ctx.Value(appctx.GeminiTokenCTXKey).(string) - if !ok || len(token) == 0 { - return nil, ErrInvalidToken - } - - client, err := clients.NewWithProxy("gemini", serverURL, token, proxyURL) - if err != nil { - return nil, err - } - return NewClientWithPrometheus(&HTTPClient{client}, "gemini_client"), err -} - -// New returns a new HTTPClient, retrieving the base URL from the environment -func New() (Client, error) { - serverEnvKey := "GEMINI_SERVER" - serverURL := os.Getenv(serverEnvKey) - if len(serverURL) == 0 { - return nil, errors.New(serverEnvKey + " was empty") - } - proxy := os.Getenv("HTTP_PROXY") - client, err := clients.NewWithProxy("gemini", serverURL, os.Getenv("GEMINI_TOKEN"), proxy) - if err != nil { - return nil, err - } - return NewClientWithPrometheus(&HTTPClient{client}, "gemini_client"), err -} - -// isB64 - check if the input string is base64 -func isB64(s string) bool { - // will get an error if we fail to decode - _, err := base64.StdEncoding.DecodeString(s) - return err == nil -} - -func setHeaders( - req *http.Request, - APIKey string, - signer *cryptography.HMACKey, - payload string, - submitType string, -) error { - req.Header.Set("Content-Type", "text/plain") - req.Header.Set("Content-Length", "0") - req.Header.Set("Cache-Control", "no-cache") - if payload != "" { - if !isB64(payload) { - // payload is not base64, so encode it before sending - payload = base64.StdEncoding.EncodeToString([]byte(payload)) - } - // base64 encode the payload - req.Header.Set("X-GEMINI-PAYLOAD", payload) - } - if submitType != "oauth" { - // do not send when oauth - req.Header.Set("X-GEMINI-APIKEY", APIKey) - } - return setPrivateRequestHeaders( - req, - APIKey, - signer, - payload, - submitType, - ) -} - -func setPrivateRequestHeaders( - req *http.Request, - APIKey string, - signer *cryptography.HMACKey, - payload string, - submitType string, -) error { - if submitType == "hmac" { - if signer == nil { - return errors.New("GEMINI_SUBMIT_TYPE set to 'hmac' but no signer provided") - } - signs := *signer - if !isB64(payload) { - payload = base64.StdEncoding.EncodeToString([]byte(payload)) - } - // only set if sending an hmac salt - signature, err := signs.HMACSha384([]byte(payload)) - if err != nil { - return err - } - req.Header.Set("X-GEMINI-SIGNATURE", hex.EncodeToString(signature)) - } - return nil -} - -// CheckTxStatus uploads the bulk payout for gemini -func (c *HTTPClient) CheckTxStatus(ctx context.Context, APIKey string, clientID string, txRef string) (*PayoutResult, error) { - urlPath := fmt.Sprintf("/v1/payment/%s/%s", clientID, txRef) - req, err := c.client.NewRequest(ctx, "GET", urlPath, nil, nil) - if err != nil { - return nil, err - } - - // create the gemini payload - payload, err := json.Marshal(NewCheckTxPayload(urlPath)) - if err != nil { - return nil, fmt.Errorf("failed to create gemini payload for api: %w", err) - } - - // get api secret from context - apiSecret, err := appctx.GetStringFromContext(ctx, appctx.GeminiAPISecretCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get gemini signing secret from ctx: %w", err) - } - //create a new hmac hasher - signer := cryptography.NewHMACHasher([]byte(apiSecret)) - - err = setHeaders(req, APIKey, &signer, string(payload), "hmac") - if err != nil { - return nil, err - } - - var body PayoutResult - _, err = c.client.Do(ctx, req, &body) - if err != nil { - var eb *errorutils.ErrorBundle - if errors.As(err, &eb) { - if httpState, ok := eb.Data().(clients.HTTPState); ok { - if httpState.Status == http.StatusNotFound { - notFoundReason := "404 From Gemini" - return &PayoutResult{ - Result: "Error", - Reason: ¬FoundReason, - TxRef: txRef, - }, nil - } - } - } - return nil, err - } - - return &body, err -} - -// UploadBulkPayout uploads the bulk payout for gemini -func (c *HTTPClient) UploadBulkPayout(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]PayoutResult, error) { - - req, err := c.client.NewRequest(ctx, "POST", "/v1/payments/bulkPay", nil, nil) - if err != nil { - return nil, err - } - err = setHeaders(req, APIKey, &signer, payload, os.Getenv("GEMINI_SUBMIT_TYPE")) - if err != nil { - return nil, err - } - - var body []PayoutResult - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - - return &body, err -} - -// ValidateAccountReq - request structure for inputs to validate account client call -type ValidateAccountReq struct { - Token string `url:"token"` - RecipientID string `url:"recipient_id"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (v *ValidateAccountReq) GenerateQueryString() (url.Values, error) { - return query.Values(v) -} - -// ValidatedAccount represents an account that has been validated by Gemini. -type ValidatedAccount struct { - ID string `json:"id"` - CountryCode string `json:"countryCode"` - ValidDocuments []ValidDocument `json:"validDocuments"` -} - -// ValidDocument represent a valid proof of identity document type. -type ValidDocument struct { - Type string `json:"type"` - IssuingCountry string `json:"issuingCountry"` -} - -func (c *HTTPClient) FetchValidatedAccount(ctx context.Context, verificationToken, recipientID string) (ValidatedAccount, error) { - req, err := c.client.NewRequest(ctx, http.MethodPost, "/v1/account/validate", nil, &ValidateAccountReq{ - Token: verificationToken, - RecipientID: recipientID, - }) - if err != nil { - return ValidatedAccount{}, err - } - - var res ValidatedAccount - if _, err := c.client.Do(ctx, req, &res); err != nil { - return ValidatedAccount{}, err - } - - return res, nil -} - -// FetchAccountList fetches the list of accounts associated with the given api key -func (c *HTTPClient) FetchAccountList( - ctx context.Context, - APIKey string, - signer cryptography.HMACKey, - payload string, -) (*[]Account, error) { - req, err := c.client.NewRequest(ctx, "POST", "/v1/account/list", nil, nil) - if err != nil { - return nil, err - } - err = setHeaders(req, APIKey, &signer, payload, os.Getenv("GEMINI_SUBMIT_TYPE")) - if err != nil { - return nil, err - } - - var body []Account - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - return &body, err -} - -// FetchBalances fetches the list of accounts associated with the given api key -func (c *HTTPClient) FetchBalances( - ctx context.Context, - APIKey string, - signer cryptography.HMACKey, - payload string, -) (*[]Balance, error) { - req, err := c.client.NewRequest(ctx, "POST", "/v1/balances", nil, nil) - if err != nil { - return nil, err - } - err = setHeaders(req, APIKey, &signer, payload, os.Getenv("GEMINI_SUBMIT_TYPE")) - if err != nil { - return nil, err - } - - var body []Balance - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - return &body, err -} diff --git a/libs/clients/gemini/client_test.go b/libs/clients/gemini/client_test.go deleted file mode 100644 index 970bc27bb..000000000 --- a/libs/clients/gemini/client_test.go +++ /dev/null @@ -1,132 +0,0 @@ -//go:build integration && vpn -// +build integration,vpn - -package gemini - -import ( - "context" - "encoding/base64" - "encoding/json" - "os" - "strings" - "testing" - - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/custodian" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type GeminiTestSuite struct { - suite.Suite - secret cryptography.HMACKey - apikey string -} - -func TestGeminiTestSuite(t *testing.T) { - suite.Run(t, new(GeminiTestSuite)) -} - -func (suite *GeminiTestSuite) SetupTest() { - secret := os.Getenv("GEMINI_CLIENT_SECRET") - apikey := os.Getenv("GEMINI_CLIENT_KEY") - if secret != "" { - suite.secret = cryptography.NewHMACHasher([]byte(secret)) - suite.apikey = apikey - } -} - -func (suite *GeminiTestSuite) TestBulkPay() { - ctx := context.Background() - client, err := New() - suite.Require().NoError(err, "Must be able to correctly initialize the client") - five := decimal.NewFromFloat(5) - - var accountKey *string - if strings.Split(suite.apikey, "-")[0] == "master" { - accountListRequest := suite.preparePrivateRequest(NewAccountListPayload()) - accounts, err := client.FetchAccountList(ctx, suite.apikey, suite.secret, accountListRequest) - suite.Require().NoError(err, "should not error during account list fetching") - primary := "primary" - account := findAccountByClass(accounts, primary) - suite.Require().Equal(primary, account.Class, "should have a primary account") - accountKey = &primary - } - - balancesRequest := suite.preparePrivateRequest(NewBalancesPayload(accountKey)) - balances, err := client.FetchBalances(ctx, suite.apikey, suite.secret, balancesRequest) - suite.Require().NoError(err, "should not error during balances fetching") - balance := findBalanceByCurrency(balances, "BAT") - suite.Require().True( - balance.Available.GreaterThanOrEqual(five), - "must have at least 5 bat to pass the rest of the test", - ) - - tx := custodian.Transaction{ - // use this settlement id to create an ephemeral test - SettlementID: uuid.NewV4().String(), - Destination: os.Getenv("GEMINI_TEST_DESTINATION_ID"), - Channel: "brave.com", - } - BAT := "BAT" - payouts := []PayoutPayload{{ - TxRef: GenerateTxRef(&tx), - Amount: five, - Currency: BAT, - Destination: tx.Destination, - }} - bulkPayoutRequest := suite.preparePrivateRequest(NewBulkPayoutPayload( - accountKey, - os.Getenv("GEMINI_CLIENT_ID"), - &payouts, - )) - - bulkPayoutResponse, err := client.UploadBulkPayout(ctx, suite.apikey, suite.secret, bulkPayoutRequest) - pendingStatus := "Pending" - expectedPayoutResult := PayoutResult{ - Result: "OK", - TxRef: GenerateTxRef(&tx), - Amount: &five, - Currency: &BAT, - Destination: &tx.Destination, - Status: &pendingStatus, - } - expectedPayoutResults := []PayoutResult{expectedPayoutResult} - suite.Require().NoError(err, "should not error during bulk payout uploading") - suite.Require().Equal(&expectedPayoutResults, bulkPayoutResponse, "the response should be predictable") - - status, err := client.CheckTxStatus( - ctx, - suite.apikey, - os.Getenv("GEMINI_CLIENT_ID"), - GenerateTxRef(&tx), - ) - suite.Require().NoError(err, "should not error during bulk payout uploading") - suite.Require().Equal(&expectedPayoutResult, status, "checking the single response should be predictable") -} - -func findBalanceByCurrency(balances *[]Balance, currency string) Balance { - for _, balance := range *balances { - if balance.Currency == currency { - return balance - } - } - return Balance{} -} - -func findAccountByClass(accounts *[]Account, typ string) Account { - for _, account := range *accounts { - if account.Class == typ { - return account - } - } - return Account{} -} - -func (suite *GeminiTestSuite) preparePrivateRequest(payload interface{}) string { - payloadSerialized, err := json.Marshal(payload) - suite.Require().NoError(err, "payload must be able to be serialized") - - return base64.StdEncoding.EncodeToString(payloadSerialized) -} diff --git a/libs/clients/gemini/instrumented_client.go b/libs/clients/gemini/instrumented_client.go deleted file mode 100755 index 4294c2e79..000000000 --- a/libs/clients/gemini/instrumented_client.go +++ /dev/null @@ -1,110 +0,0 @@ -package gemini - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "gemini_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// CheckTxStatus implements Client -func (_d ClientWithPrometheus) CheckTxStatus(ctx context.Context, APIKEY string, clientID string, txRef string) (pp1 *PayoutResult, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CheckTxStatus", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CheckTxStatus(ctx, APIKEY, clientID, txRef) -} - -// FetchAccountList implements Client -func (_d ClientWithPrometheus) FetchAccountList(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (aap1 *[]Account, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchAccountList", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchAccountList(ctx, APIKey, signer, payload) -} - -// FetchBalances implements Client -func (_d ClientWithPrometheus) FetchBalances(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (bap1 *[]Balance, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchBalances", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchBalances(ctx, APIKey, signer, payload) -} - -// FetchValidatedAccount implements Client -func (_d ClientWithPrometheus) FetchValidatedAccount(ctx context.Context, verificationToken string, recipientID string) (v1 ValidatedAccount, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchValidatedAccount", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchValidatedAccount(ctx, verificationToken, recipientID) -} - -// UploadBulkPayout implements Client -func (_d ClientWithPrometheus) UploadBulkPayout(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (pap1 *[]PayoutResult, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "UploadBulkPayout", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UploadBulkPayout(ctx, APIKey, signer, payload) -} diff --git a/libs/clients/gemini/mock/mock.go b/libs/clients/gemini/mock/mock.go deleted file mode 100644 index 085bdd523..000000000 --- a/libs/clients/gemini/mock/mock.go +++ /dev/null @@ -1,112 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/gemini/client.go - -// Package mock_gemini is a generated GoMock package. -package mock_gemini - -import ( - context "context" - reflect "reflect" - - gemini "github.com/brave-intl/bat-go/libs/clients/gemini" - cryptography "github.com/brave-intl/bat-go/libs/cryptography" - gomock "github.com/golang/mock/gomock" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// CheckTxStatus mocks base method. -func (m *MockClient) CheckTxStatus(ctx context.Context, APIKEY, clientID, txRef string) (*gemini.PayoutResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckTxStatus", ctx, APIKEY, clientID, txRef) - ret0, _ := ret[0].(*gemini.PayoutResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CheckTxStatus indicates an expected call of CheckTxStatus. -func (mr *MockClientMockRecorder) CheckTxStatus(ctx, APIKEY, clientID, txRef interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckTxStatus", reflect.TypeOf((*MockClient)(nil).CheckTxStatus), ctx, APIKEY, clientID, txRef) -} - -// FetchAccountList mocks base method. -func (m *MockClient) FetchAccountList(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]gemini.Account, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchAccountList", ctx, APIKey, signer, payload) - ret0, _ := ret[0].(*[]gemini.Account) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchAccountList indicates an expected call of FetchAccountList. -func (mr *MockClientMockRecorder) FetchAccountList(ctx, APIKey, signer, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchAccountList", reflect.TypeOf((*MockClient)(nil).FetchAccountList), ctx, APIKey, signer, payload) -} - -// FetchBalances mocks base method. -func (m *MockClient) FetchBalances(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]gemini.Balance, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchBalances", ctx, APIKey, signer, payload) - ret0, _ := ret[0].(*[]gemini.Balance) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchBalances indicates an expected call of FetchBalances. -func (mr *MockClientMockRecorder) FetchBalances(ctx, APIKey, signer, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchBalances", reflect.TypeOf((*MockClient)(nil).FetchBalances), ctx, APIKey, signer, payload) -} - -// FetchValidateAccount mocks base method. -func (m *MockClient) FetchValidatedAccount(ctx context.Context, verificationToken, recipientID string) (gemini.ValidatedAccount, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchValidatedAccount", ctx, verificationToken, recipientID) - ret0, _ := ret[0].(gemini.ValidatedAccount) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchValidateAccount indicates an expected call of FetchValidateAccount. -func (mr *MockClientMockRecorder) FetchValidateAccount(ctx, verificationToken, recipientID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchValidatedAccount", reflect.TypeOf((*MockClient)(nil).FetchValidatedAccount), ctx, verificationToken, recipientID) -} - -// UploadBulkPayout mocks base method. -func (m *MockClient) UploadBulkPayout(ctx context.Context, APIKey string, signer cryptography.HMACKey, payload string) (*[]gemini.PayoutResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UploadBulkPayout", ctx, APIKey, signer, payload) - ret0, _ := ret[0].(*[]gemini.PayoutResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UploadBulkPayout indicates an expected call of UploadBulkPayout. -func (mr *MockClientMockRecorder) UploadBulkPayout(ctx, APIKey, signer, payload interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadBulkPayout", reflect.TypeOf((*MockClient)(nil).UploadBulkPayout), ctx, APIKey, signer, payload) -} diff --git a/libs/clients/radom/instrumented.go b/libs/clients/radom/instrumented.go deleted file mode 100644 index ae1cd4c35..000000000 --- a/libs/clients/radom/instrumented.go +++ /dev/null @@ -1,50 +0,0 @@ -package radom - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" -) - -type InstrumentedClient struct { - name string - cl *Client - vec *prometheus.SummaryVec -} - -// newInstrumentedClient returns an instance of the Client decorated with prometheus summary metric. -// This function panics if it cannot register the metric. -func newInstrumentedClient(name string, cl *Client) *InstrumentedClient { - v := prometheus.NewSummaryVec(prometheus.SummaryOpts{ - Name: "radom_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}, - ) - prometheus.MustRegister(v) - - result := &InstrumentedClient{ - name: name, - cl: cl, - vec: v, - } - - return result -} - -func (_d *InstrumentedClient) CreateCheckoutSession(ctx context.Context, cp1 *CheckoutSessionRequest) (cp2 *CheckoutSessionResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - _d.vec.WithLabelValues(_d.name, "CreateCheckoutSession", result).Observe(time.Since(_since).Seconds()) - }() - - return _d.cl.CreateCheckoutSession(ctx, cp1) -} diff --git a/libs/clients/radom/mock.go b/libs/clients/radom/mock.go deleted file mode 100644 index 3205ccb35..000000000 --- a/libs/clients/radom/mock.go +++ /dev/null @@ -1,20 +0,0 @@ -package radom - -import ( - "context" -) - -type MockClient struct { - FnCreateCheckoutSession func(ctx context.Context, req *CheckoutSessionRequest) (*CheckoutSessionResponse, error) -} - -func (c *MockClient) CreateCheckoutSession( - ctx context.Context, - req *CheckoutSessionRequest, -) (*CheckoutSessionResponse, error) { - if c.FnCreateCheckoutSession == nil { - return &CheckoutSessionResponse{}, nil - } - - return c.FnCreateCheckoutSession(ctx, req) -} diff --git a/libs/clients/radom/radom.go b/libs/clients/radom/radom.go deleted file mode 100644 index 0efe49545..000000000 --- a/libs/clients/radom/radom.go +++ /dev/null @@ -1,212 +0,0 @@ -package radom - -import ( - "context" - "crypto/subtle" - "errors" - "time" - - "github.com/shopspring/decimal" - - "github.com/brave-intl/bat-go/libs/clients" - appctx "github.com/brave-intl/bat-go/libs/context" -) - -var ( - ErrInvalidMetadataKey = errors.New("invalid metadata key") -) - -// CheckoutSessionRequest represents a request to create a checkout session. -type CheckoutSessionRequest struct { - SuccessURL string `json:"successUrl"` - CancelURL string `json:"cancelUrl"` - Currency string `json:"currency"` - ExpiresAt int64 `json:"expiresAt"` // in unix seconds - LineItems []LineItem `json:"lineItems"` - Metadata Metadata `json:"metadata"` - Customizations map[string]interface{} `json:"customizations"` - Total decimal.Decimal `json:"total"` - Gateway Gateway `json:"gateway"` -} - -// Gateway provides access to managed services configurations -type Gateway struct { - Managed Managed `json:"managed"` -} - -// Managed is the Radom managed services configuration -type Managed struct { - Methods []Method `json:"methods"` -} - -// Method is a Radom payment method type -type Method struct { - Network string `json:"network"` - Token string `json:"token"` -} - -// CheckoutSessionResponse represents the result of submission of a checkout session. -type CheckoutSessionResponse struct { - SessionID string `json:"checkoutSessionId"` - SessionURL string `json:"checkoutSessionUrl"` -} - -// LineItem is a line item for a checkout session request. -type LineItem struct { - ProductID string `json:"productId"` - ItemData map[string]interface{} `json:"itemData"` -} - -// Metadata represents metaadata in a checkout session request. -type Metadata []KeyValue - -// Get allows returns a value based on the key from the Radom metadata. -func (m Metadata) Get(key string) (string, error) { - for _, v := range m { - if subtle.ConstantTimeCompare([]byte(key), []byte(v.Key)) == 1 { - return v.Value, nil - } - } - - return "", ErrInvalidMetadataKey -} - -// KeyValue represents a key-value metadata pair. -type KeyValue struct { - Key string `json:"key"` - Value string `json:"value"` -} - -// AutomatedEVMSubscripton defines an automated subscription -type AutomatedEVMSubscription struct { - BuyerAddress string `json:"buyerAddress"` - SubscriptionContractAddress string `json:"subscriptionContractAddress"` -} - -// Subscription is a radom subscription -type Subscription struct { - AutomatedEVMSubscription AutomatedEVMSubscription `json:"automatedEVMSubscription"` -} - -// NewSubscriptionData provides details about the new subscription -type NewSubscriptionData struct { - SubscriptionID string `json:"subscriptionId"` - Subscription Subscription `json:"subscriptionType"` - Network string `json:"network"` - Token string `json:"token"` - Amount decimal.Decimal `json:"amount"` - Currency string `json:"currency"` - Period string `json:"period"` - PeriodCustomDuration string `json:"periodCustomDuration"` - CreatedAt *time.Time `json:"createdAt"` - Tags map[string]string `json:"tags"` -} - -// Data is radom specific data attached to webhook calls -type Data struct { - CheckoutSession CheckoutSession `json:"checkoutSession"` -} - -// CheckoutSession describes a radom checkout session -type CheckoutSession struct { - CheckoutSessionID string `json:"checkoutSessionId"` - Metadata Metadata `json:"metadata"` -} - -// ManagedRecurringPayment provides details about the recurring payment from webhook -type ManagedRecurringPayment struct { - PaymentMethod Method `json:"paymentMethod"` - Amount decimal.Decimal `json:"amount"` -} - -// EventData encapsulates the webhook event -type EventData struct { - ManagedRecurringPayment *ManagedRecurringPayment `json:"managedRecurringPayment"` - NewSubscription *NewSubscriptionData `json:"newSubscription"` -} - -// WebhookRequest represents a radom webhook submission -type WebhookRequest struct { - EventType string `json:"eventType"` - EventData EventData `json:"eventData"` - Data Data `json:"radomData"` -} - -// Client communicates with Radom. -type Client struct { - client *clients.SimpleHTTPClient - gwMethodsProd []Method - gwMethods []Method -} - -// New returns a ready to use Client. -func New(srvURL, secret, proxyAddr string) (*Client, error) { - return newClient(srvURL, secret, proxyAddr) -} - -func NewInstrumented(srvURL, secret, proxyAddr string) (*InstrumentedClient, error) { - cl, err := newClient(srvURL, secret, proxyAddr) - if err != nil { - return nil, err - } - - return newInstrumentedClient("radom_client", cl), nil -} - -func newClient(srvURL, secret, proxyAddr string) (*Client, error) { - client, err := clients.NewWithProxy("radom", srvURL, secret, proxyAddr) - if err != nil { - return nil, err - } - - result := &Client{ - client: client, - gwMethodsProd: []Method{ - { - Network: "Polygon", - Token: "0x3cef98bb43d732e2f285ee605a8158cde967d219", - }, - - { - Network: "Ethereum", - Token: "0x0d8775f648430679a709e98d2b0cb6250d2887ef", - }, - }, - gwMethods: []Method{ - { - Network: "SepoliaTestnet", - Token: "0x5D684d37922dAf7Aa2013E65A22880a11C475e25", - }, - { - Network: "PolygonTestnet", - Token: "0xd445cAAbb9eA6685D3A512439256866563a16E93", - }, - }, - } - - return result, nil -} - -// CreateCheckoutSession creates a Radom checkout session. -func (c *Client) CreateCheckoutSession( - ctx context.Context, - req *CheckoutSessionRequest, -) (*CheckoutSessionResponse, error) { - // Get the environment so we know what is acceptable chain/tokens. - methods := c.methodsForEnv(ctx) - - req.Gateway = Gateway{ - Managed: Managed{Methods: methods}, - } - - return nil, errors.New("not implemented") -} - -func (c *Client) methodsForEnv(ctx context.Context) []Method { - env, ok := ctx.Value(appctx.EnvironmentCTXKey).(string) - if !ok || env != "production" { - return c.gwMethods - } - - return c.gwMethodsProd -} diff --git a/libs/clients/ratios/client.go b/libs/clients/ratios/client.go deleted file mode 100644 index 81492e0e9..000000000 --- a/libs/clients/ratios/client.go +++ /dev/null @@ -1,141 +0,0 @@ -package ratios - -import ( - "context" - "errors" - "fmt" - "net/url" - "os" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/coingecko" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/google/go-querystring/query" - cache "github.com/patrickmn/go-cache" - "github.com/shopspring/decimal" -) - -// Client abstracts over the underlying client -type Client interface { - FetchRate(ctx context.Context, base string, currency string) (*RateResponse, error) -} - -// HTTPClient wraps http.Client for interacting with the ratios server -type HTTPClient struct { - client *clients.SimpleHTTPClient - cache *cache.Cache -} - -// NewWithContext returns a new HTTPClient, retrieving the base URL from the context -func NewWithContext(ctx context.Context) (Client, error) { - // get the server url from context - serverURL, err := appctx.GetStringFromContext(ctx, appctx.RatiosServerCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get RatiosServer from context: %w", err) - } - - // get the server access token from context - accessToken, err := appctx.GetStringFromContext(ctx, appctx.RatiosAccessTokenCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get RatiosAccessToken from context: %w", err) - } - - client, err := clients.New(serverURL, accessToken) - if err != nil { - return nil, err - } - - // get default timeout and purge from context - expires, err := appctx.GetDurationFromContext(ctx, appctx.RatiosCacheExpiryDurationCTXKey) - if err != nil { - expires = 5 * time.Second - } - - // get default purge and purge from context - purge, err := appctx.GetDurationFromContext(ctx, appctx.RatiosCachePurgeDurationCTXKey) - if err != nil { - purge = 1 * time.Minute - } - - return NewClientWithPrometheus( - &HTTPClient{ - client: client, - cache: cache.New(expires, purge), - }, "ratios_context_client"), nil -} - -// New returns a new HTTPClient, retrieving the base URL from the environment -func New() (Client, error) { - serverEnvKey := "RATIOS_SERVICE" - serverURL := os.Getenv(serverEnvKey) - if len(serverURL) == 0 { - return nil, errors.New(serverEnvKey + " was empty") - } - client, err := clients.New(serverURL, os.Getenv("RATIOS_TOKEN")) - if err != nil { - return nil, err - } - return NewClientWithPrometheus( - &HTTPClient{ - client: client, - cache: cache.New(5*time.Second, 1*time.Minute), - }, "ratios_client"), err -} - -// RateResponse is the response received from ratios -type RateResponse struct { - LastUpdated time.Time `json:"lastUpdated"` - Payload map[string]decimal.Decimal `json:"payload"` -} - -// FetchOptions options for fetching rates from ratios -type FetchOptions struct { - Currency string `url:"currency,omitempty"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (fo *FetchOptions) GenerateQueryString() (url.Values, error) { - return query.Values(fo) -} - -// RelativeResponse - the relative response structure -type RelativeResponse struct { - Payload coingecko.SimplePriceResponse `json:"payload"` - LastUpdated time.Time `json:"lastUpdated"` -} - -// FetchRate fetches the rate of a currency to BAT -func (c *HTTPClient) FetchRate(ctx context.Context, base string, currency string) (*RateResponse, error) { - // normalize base and currency to lowercase - base = strings.ToLower(base) - currency = strings.ToLower(currency) - - var cacheKey = fmt.Sprintf("%s_%s", base, currency) - // check cache for this rate - if rate, found := c.cache.Get(cacheKey); found { - return rate.(*RateResponse), nil - } - - url := fmt.Sprintf("/v2/relative/provider/coingecko/%s/%s/1d", base, currency) - req, err := c.client.NewRequest(ctx, "GET", url, nil, nil) - if err != nil { - return nil, err - } - - var body RelativeResponse - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - - resp := RateResponse{ - Payload: body.Payload[base], - LastUpdated: time.Now(), - } - - c.cache.Set(cacheKey, &resp, cache.DefaultExpiration) - - return &resp, nil -} diff --git a/libs/clients/ratios/client_test.go b/libs/clients/ratios/client_test.go deleted file mode 100644 index 5d7b08345..000000000 --- a/libs/clients/ratios/client_test.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build ratiosintegration -// +build ratiosintegration - -package ratios - -import ( - "context" - "errors" - "net/http" - "testing" - - "github.com/brave-intl/bat-go/libs/clients" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/stretchr/testify/assert" -) - -func TestFetchRate(t *testing.T) { - ctx := context.Background() - - client, err := New() - assert.NoError(t, err, "Must be able to correctly initialize the client") - - resp, err := client.FetchRate(ctx, "unknown", "unknown") - assert.Error(t, err, "should get an error back") - var rateResponse *RateResponse - assert.Equal(t, rateResponse, resp, "should be nil when error returned") - - var bundle *errorutils.ErrorBundle - errors.As(err, &bundle) - response, ok := bundle.Data().(clients.HTTPState) - assert.Equal(t, true, ok) - assert.Equal(t, http.StatusNotFound, response.Status) -} diff --git a/libs/clients/ratios/instrumented_client.go b/libs/clients/ratios/instrumented_client.go deleted file mode 100755 index 6b1e06ebf..000000000 --- a/libs/clients/ratios/instrumented_client.go +++ /dev/null @@ -1,53 +0,0 @@ -package ratios - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "ratios_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// FetchRate implements Client -func (_d ClientWithPrometheus) FetchRate(ctx context.Context, base string, currency string) (rp1 *RateResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "FetchRate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.FetchRate(ctx, base, currency) -} diff --git a/libs/clients/ratios/mock/mock.go b/libs/clients/ratios/mock/mock.go deleted file mode 100644 index 63f848763..000000000 --- a/libs/clients/ratios/mock/mock.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/ratios/client.go - -// Package mock_ratios is a generated GoMock package. -package mock_ratios - -import ( - context "context" - reflect "reflect" - - ratios "github.com/brave-intl/bat-go/libs/clients/ratios" - gomock "github.com/golang/mock/gomock" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// FetchRate mocks base method. -func (m *MockClient) FetchRate(ctx context.Context, base, currency string) (*ratios.RateResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchRate", ctx, base, currency) - ret0, _ := ret[0].(*ratios.RateResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchRate indicates an expected call of FetchRate. -func (mr *MockClientMockRecorder) FetchRate(ctx, base, currency interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchRate", reflect.TypeOf((*MockClient)(nil).FetchRate), ctx, base, currency) -} diff --git a/libs/clients/reputation/client.go b/libs/clients/reputation/client.go deleted file mode 100644 index 17910c38a..000000000 --- a/libs/clients/reputation/client.go +++ /dev/null @@ -1,358 +0,0 @@ -package reputation - -import ( - "context" - "errors" - "fmt" - "net/http" - "net/url" - "os" - - "github.com/brave-intl/bat-go/libs/clients" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/google/go-querystring/query" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// Client abstracts over the underlying client -type Client interface { - IsWalletReputable(ctx context.Context, id uuid.UUID, platform string) (bool, error) - IsWalletAdsReputable(ctx context.Context, id uuid.UUID, platform string) (bool, error) - IsDrainReputable(ctx context.Context, id, promotionID uuid.UUID, withdrawAmount decimal.Decimal) (bool, []int, error) - IsLinkingReputable(ctx context.Context, id uuid.UUID, country string) (bool, []int, error) - IsWalletOnPlatform(ctx context.Context, id uuid.UUID, platform string) (bool, error) - UpsertReputationSummary(ctx context.Context, paymentID, geoCountry string) error - UpdateReputationSummary(ctx context.Context, paymentID string, verifiedWallet bool) error -} - -// HTTPClient wraps http.Client for interacting with the reputation server -type HTTPClient struct { - client *clients.SimpleHTTPClient -} - -// New returns a new HTTPClient, retrieving the base URL from the -// environment -func New() (Client, error) { - serverEnvKey := "REPUTATION_SERVER" - serverURL := os.Getenv(serverEnvKey) - - if len(serverURL) == 0 { - if os.Getenv("ENV") != "local" { - return nil, errors.New("REPUTATION_SERVER is missing in production environment") - } - return nil, errors.New(serverEnvKey + " was empty") - } - - client, err := clients.New(serverURL, os.Getenv("REPUTATION_TOKEN")) - if err != nil { - return nil, err - } - - return NewClientWithPrometheus(&HTTPClient{client}, "reputation_client"), nil -} - -// IsDrainReputableOpts - the query string options for the is reputable api call -type IsDrainReputableOpts struct { - WithdrawalAmount string `url:"withdrawal_amount"` - PromotionID string `url:"promotion_id"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (iro *IsDrainReputableOpts) GenerateQueryString() (url.Values, error) { - return query.Values(iro) -} - -// IsReputableResponse is what the reputation server -// will send back when we ask if a wallet is reputable -type IsReputableResponse struct { - Cohorts []int `json:"cohorts"` -} - -var ( - // CohortNil - bad cohort - CohortNil int - // CohortOK - ok cohort - CohortOK = 1 - // CohortTooYoung - too young cohort - CohortTooYoung = 2 - // CohortWithdrawalLimits - limited cohort - CohortWithdrawalLimits = 4 - // CohortGeoResetDifferent - different geo than orig - CohortGeoResetDifferent = 7 -) - -// IsLinkingReputableRequestQSB - query string "body" for is linking reputable requests -type IsLinkingReputableRequestQSB struct { - Country string `url:"country,omitempty"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (ilrrqsb *IsLinkingReputableRequestQSB) GenerateQueryString() (url.Values, error) { - return query.Values(ilrrqsb) -} - -// IsLinkingReputable makes the request to the reputation server -// and returns whether a paymentId has enough reputation -// to claim a grant -func (c *HTTPClient) IsLinkingReputable( - ctx context.Context, - paymentID uuid.UUID, - country string, -) (bool, []int, error) { - - req, err := c.client.NewRequest( - ctx, - "GET", - "v2/reputation/"+paymentID.String()+"/grants", - nil, - &IsLinkingReputableRequestQSB{Country: country}, - ) - if err != nil { - return false, []int{CohortNil}, err - } - - var resp IsReputableResponse - _, err = c.client.Do(ctx, req, &resp) - if err != nil { - return false, []int{CohortNil}, err - } - - // okay to be too young for drain reputable - // must also be ok - - for _, v := range resp.Cohorts { - if v == CohortOK { - return true, resp.Cohorts, nil - } - } - return false, resp.Cohorts, nil -} - -// IsDrainReputable makes the request to the reputation server -// and returns whether a paymentId has enough reputation -// to claim a grant -func (c *HTTPClient) IsDrainReputable( - ctx context.Context, - paymentID, promotionID uuid.UUID, - withdrawalAmount decimal.Decimal, -) (bool, []int, error) { - - var body = IsDrainReputableOpts{ - WithdrawalAmount: withdrawalAmount.String(), - PromotionID: promotionID.String(), - } - - req, err := c.client.NewRequest( - ctx, - "GET", - "v2/reputation/"+paymentID.String()+"/grants", - nil, - &body, - ) - if err != nil { - return false, []int{CohortNil}, err - } - - var resp IsReputableResponse - _, err = c.client.Do(ctx, req, &resp) - if err != nil { - return false, []int{CohortNil}, err - } - - // okay to be too young for drain reputable - // must also be ok - - for _, v := range resp.Cohorts { - if v == CohortOK { - return true, resp.Cohorts, nil - } - } - return false, resp.Cohorts, nil -} - -// IsWalletReputableResponse is what the reputation server -// will send back when we ask if a wallet is reputable -type IsWalletReputableResponse struct { - IsReputable bool `json:"isReputable"` -} - -// IsReputableOpts - the query string options for the is reputable api call -type IsReputableOpts struct { - Platform string `url:"platform"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (iro *IsReputableOpts) GenerateQueryString() (url.Values, error) { - return query.Values(iro) -} - -// IsWalletAdsReputable makes the request to the reputation server -// and returns whether a paymentId has enough reputation -// to claim a grant -func (c *HTTPClient) IsWalletAdsReputable( - ctx context.Context, - paymentID uuid.UUID, - platform string, -) (bool, error) { - - var body IsReputableOpts - if platform != "" { - // pass in query string "platform" into our request - body = IsReputableOpts{ - Platform: platform, - } - } - - req, err := c.client.NewRequest( - ctx, - "GET", - "v1/reputation/"+paymentID.String()+"/ads", - nil, - &body, - ) - if err != nil { - return false, err - } - - var resp IsWalletReputableResponse - _, err = c.client.Do(ctx, req, &resp) - if err != nil { - return false, err - } - - return resp.IsReputable, nil -} - -// IsWalletReputable makes the request to the reputation server -// and returns whether a paymentId has enough reputation -// to claim a grant -func (c *HTTPClient) IsWalletReputable( - ctx context.Context, - paymentID uuid.UUID, - platform string, -) (bool, error) { - - var body IsReputableOpts - if platform != "" { - // pass in query string "platform" into our request - body = IsReputableOpts{ - Platform: platform, - } - } - - req, err := c.client.NewRequest( - ctx, - "GET", - "v1/reputation/"+paymentID.String(), - nil, - &body, - ) - if err != nil { - return false, err - } - - var resp IsWalletReputableResponse - _, err = c.client.Do(ctx, req, &resp) - if err != nil { - return false, err - } - - return resp.IsReputable, nil -} - -// IsWalletOnPlatformResponse - will send back indication if wallet is on said platform -type IsWalletOnPlatformResponse struct { - IsOnPlatform bool `json:"isOnPlatform"` -} - -// IsWalletOnPlatformOpts - the query string options for the is reputable api call -type IsWalletOnPlatformOpts struct { - PriorTo string `url:"priorTo"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (iwopo *IsWalletOnPlatformOpts) GenerateQueryString() (url.Values, error) { - return query.Values(iwopo) -} - -// IsWalletOnPlatform makes the request to the reputation server -// and returns whether a paymentId is on a given platform -func (c *HTTPClient) IsWalletOnPlatform( - ctx context.Context, - paymentID uuid.UUID, - platform string, -) (bool, error) { - - if platform == "" { - return false, errors.New("need to specify the platform") - } - - req, err := c.client.NewRequest( - ctx, - "GET", - fmt.Sprintf("v1/on-platform/%s/%s", platform, paymentID.String()), - nil, - &IsWalletOnPlatformOpts{ - PriorTo: ctx.Value(appctx.WalletOnPlatformPriorToCTXKey).(string), - }, - ) - if err != nil { - return false, err - } - - var resp IsWalletOnPlatformResponse - _, err = c.client.Do(ctx, req, &resp) - if err != nil { - return false, err - } - - return resp.IsOnPlatform, nil -} - -type reputationSummaryRequest struct { - GeoCountry string `json:"geoCountry"` -} - -// UpsertReputationSummary calls the reputation summary upsert endpoint and creates or updates the reputation -// summary identified by the paymentID with the given geo country. -func (c *HTTPClient) UpsertReputationSummary(ctx context.Context, paymentID, geoCountry string) error { - b := reputationSummaryRequest{ - GeoCountry: geoCountry, - } - - req, err := c.client.NewRequest(ctx, http.MethodPut, fmt.Sprintf("v1/reputation-summary/%s", paymentID), b, nil) - if err != nil { - return err - } - - _, err = c.client.Do(ctx, req, nil) - if err != nil { - return err - } - - return nil -} - -type reputationSummaryPatchRequest struct { - VerifiedWallet bool `json:"verifiedWallet"` -} - -// UpdateReputationSummary calls the reputation summary update endpoint with the values. -func (c *HTTPClient) UpdateReputationSummary(ctx context.Context, paymentID string, verifiedWallet bool) error { - r := reputationSummaryPatchRequest{ - VerifiedWallet: verifiedWallet, - } - - req, err := c.client.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("v1/reputation-summary/%s", paymentID), r, nil) - if err != nil { - return err - } - - _, err = c.client.Do(ctx, req, nil) - if err != nil { - return err - } - - return nil -} diff --git a/libs/clients/reputation/client_test.go b/libs/clients/reputation/client_test.go deleted file mode 100644 index c95031bf4..000000000 --- a/libs/clients/reputation/client_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package reputation - -import ( - "fmt" - "testing" -) - -func TestLinkingReputableRequestParam(t *testing.T) { - r := &IsLinkingReputableRequestQSB{ - Country: "US", - } - v, err := r.GenerateQueryString() - if err != nil { - t.Error("error: ", err) - } - - if v.Encode() != "country=US" { - fmt.Println(v.Encode()) - t.Error("query string is not correct") - } -} diff --git a/libs/clients/reputation/instrumented_client.go b/libs/clients/reputation/instrumented_client.go deleted file mode 100755 index e1cceea29..000000000 --- a/libs/clients/reputation/instrumented_client.go +++ /dev/null @@ -1,139 +0,0 @@ -package reputation - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "reputation_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// IsDrainReputable implements Client -func (_d ClientWithPrometheus) IsDrainReputable(ctx context.Context, id uuid.UUID, promotionID uuid.UUID, withdrawAmount decimal.Decimal) (b1 bool, ia1 []int, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "IsDrainReputable", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.IsDrainReputable(ctx, id, promotionID, withdrawAmount) -} - -// IsLinkingReputable implements Client -func (_d ClientWithPrometheus) IsLinkingReputable(ctx context.Context, id uuid.UUID, country string) (b1 bool, ia1 []int, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "IsLinkingReputable", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.IsLinkingReputable(ctx, id, country) -} - -// IsWalletAdsReputable implements Client -func (_d ClientWithPrometheus) IsWalletAdsReputable(ctx context.Context, id uuid.UUID, platform string) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "IsWalletAdsReputable", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.IsWalletAdsReputable(ctx, id, platform) -} - -// IsWalletOnPlatform implements Client -func (_d ClientWithPrometheus) IsWalletOnPlatform(ctx context.Context, id uuid.UUID, platform string) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "IsWalletOnPlatform", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.IsWalletOnPlatform(ctx, id, platform) -} - -// IsWalletReputable implements Client -func (_d ClientWithPrometheus) IsWalletReputable(ctx context.Context, id uuid.UUID, platform string) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "IsWalletReputable", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.IsWalletReputable(ctx, id, platform) -} - -// UpdateReputationSummary implements Client -func (_d ClientWithPrometheus) UpdateReputationSummary(ctx context.Context, paymentID string, verifiedWallet bool) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "UpdateReputationSummary", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpdateReputationSummary(ctx, paymentID, verifiedWallet) -} - -// UpsertReputationSummary implements Client -func (_d ClientWithPrometheus) UpsertReputationSummary(ctx context.Context, paymentID string, geoCountry string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "UpsertReputationSummary", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpsertReputationSummary(ctx, paymentID, geoCountry) -} diff --git a/libs/clients/reputation/mock/mock.go b/libs/clients/reputation/mock/mock.go deleted file mode 100644 index 68c7044b3..000000000 --- a/libs/clients/reputation/mock/mock.go +++ /dev/null @@ -1,142 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/reputation/client.go - -// Package mock_reputation is a generated GoMock package. -package mock_reputation - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - uuid "github.com/satori/go.uuid" - decimal "github.com/shopspring/decimal" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// IsDrainReputable mocks base method. -func (m *MockClient) IsDrainReputable(ctx context.Context, id, promotionID uuid.UUID, withdrawAmount decimal.Decimal) (bool, []int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsDrainReputable", ctx, id, promotionID, withdrawAmount) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].([]int) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// IsDrainReputable indicates an expected call of IsDrainReputable. -func (mr *MockClientMockRecorder) IsDrainReputable(ctx, id, promotionID, withdrawAmount interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDrainReputable", reflect.TypeOf((*MockClient)(nil).IsDrainReputable), ctx, id, promotionID, withdrawAmount) -} - -// IsLinkingReputable mocks base method. -func (m *MockClient) IsLinkingReputable(ctx context.Context, id uuid.UUID, country string) (bool, []int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsLinkingReputable", ctx, id, country) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].([]int) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// IsLinkingReputable indicates an expected call of IsLinkingReputable. -func (mr *MockClientMockRecorder) IsLinkingReputable(ctx, id, country interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsLinkingReputable", reflect.TypeOf((*MockClient)(nil).IsLinkingReputable), ctx, id, country) -} - -// IsWalletAdsReputable mocks base method. -func (m *MockClient) IsWalletAdsReputable(ctx context.Context, id uuid.UUID, platform string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsWalletAdsReputable", ctx, id, platform) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsWalletAdsReputable indicates an expected call of IsWalletAdsReputable. -func (mr *MockClientMockRecorder) IsWalletAdsReputable(ctx, id, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWalletAdsReputable", reflect.TypeOf((*MockClient)(nil).IsWalletAdsReputable), ctx, id, platform) -} - -// IsWalletOnPlatform mocks base method. -func (m *MockClient) IsWalletOnPlatform(ctx context.Context, id uuid.UUID, platform string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsWalletOnPlatform", ctx, id, platform) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsWalletOnPlatform indicates an expected call of IsWalletOnPlatform. -func (mr *MockClientMockRecorder) IsWalletOnPlatform(ctx, id, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWalletOnPlatform", reflect.TypeOf((*MockClient)(nil).IsWalletOnPlatform), ctx, id, platform) -} - -// IsWalletReputable mocks base method. -func (m *MockClient) IsWalletReputable(ctx context.Context, id uuid.UUID, platform string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsWalletReputable", ctx, id, platform) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsWalletReputable indicates an expected call of IsWalletReputable. -func (mr *MockClientMockRecorder) IsWalletReputable(ctx, id, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWalletReputable", reflect.TypeOf((*MockClient)(nil).IsWalletReputable), ctx, id, platform) -} - -// UpdateReputationSummary mocks base method. -func (m *MockClient) UpdateReputationSummary(ctx context.Context, paymentID string, verifiedWallet bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateReputationSummary", ctx, paymentID, verifiedWallet) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateReputationSummary indicates an expected call of UpdateReputationSummary. -func (mr *MockClientMockRecorder) UpdateReputationSummary(ctx, paymentID, verifiedWallet interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateReputationSummary", reflect.TypeOf((*MockClient)(nil).UpdateReputationSummary), ctx, paymentID, verifiedWallet) -} - -// UpsertReputationSummary mocks base method. -func (m *MockClient) UpsertReputationSummary(ctx context.Context, paymentID, geoCountry string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpsertReputationSummary", ctx, paymentID, geoCountry) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpsertReputationSummary indicates an expected call of UpsertReputationSummary. -func (mr *MockClientMockRecorder) UpsertReputationSummary(ctx, paymentID, geoCountry interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertReputationSummary", reflect.TypeOf((*MockClient)(nil).UpsertReputationSummary), ctx, paymentID, geoCountry) -} diff --git a/libs/clients/reputation/proxy.go b/libs/clients/reputation/proxy.go deleted file mode 100644 index a169c8f62..000000000 --- a/libs/clients/reputation/proxy.go +++ /dev/null @@ -1,34 +0,0 @@ -package reputation - -import ( - "net/http" - "net/http/httputil" - "net/url" - - "github.com/getsentry/sentry-go" - log "github.com/sirupsen/logrus" -) - -// ProxyRouter is a reverse proxy to reputation endpoints for client access -func ProxyRouter( - reputationServer string, - reputationToken string, -) http.HandlerFunc { - proxyURL, err := url.Parse(reputationServer) - if err != nil { - sentry.CaptureException(err) - log.Panic(err) - } - proxy := httputil.NewSingleHostReverseProxy(proxyURL) - proxy.Director = func(req *http.Request) { - req.Header.Add("X-Forwarded-Host", req.Host) - req.Header.Add("X-Origin-Host", proxyURL.Host) - req.Header.Add("Authorization", "Bearer "+reputationToken) - req.URL.Scheme = proxyURL.Scheme - req.URL.Host = proxyURL.Host - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - proxy.ServeHTTP(w, r) - }) -} diff --git a/libs/clients/reputation/proxy_test.go b/libs/clients/reputation/proxy_test.go deleted file mode 100644 index bc26397b6..000000000 --- a/libs/clients/reputation/proxy_test.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build integration -// +build integration - -package reputation - -import ( - "net/http" - "net/http/httptest" - "os" - "testing" -) - -func TestProxyRouter(t *testing.T) { - reputationServer := os.Getenv("REPUTATION_SERVER") - reputationToken := os.Getenv("REPUTATION_TOKEN") - if len(reputationServer) == 0 || len(reputationToken) == 0 { - t.Skip("skipping test; REPUTATION_SERVER or REPUTATION_TOKEN not set") - } - - handler := ProxyRouter(reputationServer, reputationToken) - req, err := http.NewRequest("POST", "/v1/devicecheck/attestations", nil) - if err != nil { - t.Fatal("Error creating request") - } - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - // Response should be 400, not 403 (which would indicate authorization wasn't added correctly) - if rr.Code != http.StatusBadRequest { - t.Fatalf("Response code did not match: %d != %d", http.StatusBadRequest, rr.Code) - } -} diff --git a/libs/clients/stripe/client.go b/libs/clients/stripe/client.go deleted file mode 100644 index c053279b4..000000000 --- a/libs/clients/stripe/client.go +++ /dev/null @@ -1,144 +0,0 @@ -package stripe - -import ( - "context" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/google/go-querystring/query" -) - -// Client abstracts over the underlying client -type Client interface { - CreateOnrampSession( - ctx context.Context, - integrationMode string, - walletAddress string, - sourceCurrency string, - sourceExchangeAmount string, - destinationNetwork string, - destinationCurrency string, - supportedDestinationNetworks []string, - ) (*OnrampSessionResponse, error) -} - -// HTTPClient wraps http.Client for interacting with the Stripe server -type HTTPClient struct { - client *clients.SimpleHTTPClient -} - -// NewWithContext returns a new HTTPClient, retrieving the base URL from the context -func NewWithContext(ctx context.Context) (Client, error) { - // get the server url from context - serverURL, err := appctx.GetStringFromContext(ctx, appctx.StripeOnrampServerCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get StripeServer from context: %w", err) - } - - // get the server secretKey from context - secretKey, err := appctx.GetStringFromContext(ctx, appctx.StripeOnrampSecretKeyCTXKey) - if err != nil { - return nil, fmt.Errorf("failed to get StripeSecretKey from context: %w", err) - } - - client, err := clients.NewWithHTTPClient(serverURL, secretKey, &http.Client{ - Timeout: time.Second * 30, - }) - if err != nil { - return nil, err - } - - return NewClientWithPrometheus( - &HTTPClient{ - client: client, - }, "stripe_onramp_context_client"), nil -} - -// onrampSessionParams for fetching prices -type onrampSessionParams struct { - IntegrationMode string `url:"integration_mode"` - WalletAddress string `url:"-"` - SourceCurrency string `url:"transaction_details[source_currency],omitempty"` - SourceExchangeAmount string `url:"transaction_details[source_exchange_amount],omitempty"` - DestinationNetwork string `url:"transaction_details[destination_network],omitempty"` - DestinationCurrency string `url:"transaction_details[destination_currency],omitempty"` - SupportedDestinationNetworks []string `url:"-"` -} - -// GenerateQueryString - implement the QueryStringBody interface -func (p *onrampSessionParams) GenerateQueryString() (url.Values, error) { - values, err := query.Values(p) - if err != nil { - return nil, err - } - if p.WalletAddress != "" { - key := fmt.Sprintf("transaction_details[wallet_addresses][%s]", p.DestinationNetwork) - values.Add(key, p.WalletAddress) - } - - if len(p.SupportedDestinationNetworks) > 0 { - for i, network := range p.SupportedDestinationNetworks { - key := fmt.Sprintf("transaction_details[supported_destination_networks][%d]", i) - values.Add(key, network) - } - } - - return values, nil -} - -// OnrampSessionResponse represents the response received from Stripe -type OnrampSessionResponse struct { - RedirectURL string `json:"redirect_url"` -} - -// CreateOnrampSession creates a new onramp session -func (c *HTTPClient) CreateOnrampSession( - ctx context.Context, - integrationMode string, - walletAddress string, - sourceCurrency string, - sourceExchangeAmount string, - destinationNetwork string, - destinationCurrency string, - supportedDestinationNetworks []string, -) (*OnrampSessionResponse, error) { - url := "/v1/crypto/onramp_sessions" - - params := &onrampSessionParams{ - IntegrationMode: integrationMode, - WalletAddress: walletAddress, - SourceCurrency: sourceCurrency, - SourceExchangeAmount: sourceExchangeAmount, - DestinationNetwork: destinationNetwork, - DestinationCurrency: destinationCurrency, - SupportedDestinationNetworks: supportedDestinationNetworks, - } - - values, err := params.GenerateQueryString() - if err != nil { - return nil, err - } - - req, err := c.client.NewRequest(ctx, "POST", url, nil, nil) - if err != nil { - return nil, err - } - // Override request body after req has been created since our client - // implementation only supports JSON payloads. - req.Body = ioutil.NopCloser(strings.NewReader(values.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - var body OnrampSessionResponse - _, err = c.client.Do(ctx, req, &body) - if err != nil { - return nil, err - } - - return &body, nil -} diff --git a/libs/clients/stripe/client_test.go b/libs/clients/stripe/client_test.go deleted file mode 100644 index 2153aa046..000000000 --- a/libs/clients/stripe/client_test.go +++ /dev/null @@ -1,94 +0,0 @@ -//go:build integration && vpn -// +build integration,vpn - -package stripe_test - -import ( - "context" - "os" - "testing" - - "github.com/brave-intl/bat-go/libs/clients/stripe" - appctx "github.com/brave-intl/bat-go/libs/context" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/stretchr/testify/suite" -) - -type StripeTestSuite struct { - suite.Suite - client stripe.Client - ctx context.Context -} - -func TestStripeTestSuite(t *testing.T) { - if _, exists := os.LookupEnv("STRIPE_ONRAMP_SECRET_KEY"); !exists { - t.Skip("STRIPE_ONRAMP_SECRET_KEY is not found, skipping all tests in StripeTestSuite.") - } - - suite.Run(t, new(StripeTestSuite)) -} - -var ( - stripeService string = "https://api.stripe.com/" -) - -func (suite *StripeTestSuite) SetupTest() { - // setup the context - suite.ctx = context.Background() - suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, false) - suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "info") - suite.ctx, _ = logutils.SetupLogger(suite.ctx) - - stripeKey := os.Getenv("STRIPE_ONRAMP_SECRET_KEY") - - // Set stripeKey and stripeService into context - suite.ctx = context.WithValue(suite.ctx, appctx.StripeOnrampServerCTXKey, stripeService) - suite.ctx = context.WithValue(suite.ctx, appctx.StripeOnrampSecretKeyCTXKey, stripeKey) - - var err error - suite.client, err = stripe.NewWithContext(suite.ctx) - suite.Require().NoError(err, "Must be able to correctly initialize the client") -} - -func (suite *StripeTestSuite) TestCreateOnrampSession() { - // Empty params should yield a redirect URL - var walletAddress string - var sourceCurrency string - var sourceExchangeAmount string - var destinationNetwork string - var destinationCurrency string - var supportedDestinationNetworks []string - - resp, err := suite.client.CreateOnrampSession( - suite.ctx, - "redirect", - walletAddress, - sourceCurrency, - sourceExchangeAmount, - destinationNetwork, - destinationCurrency, - supportedDestinationNetworks, - ) - suite.Require().NoError(err, "should be able to create an onramp session with no params") - suite.Require().NotEqual(resp.RedirectURL, "") - - // Filled out params should yield a redirect URL - walletAddress = "0xB00F0759DbeeF5E543Cc3E3B07A6442F5f3928a2" - sourceCurrency = "usd" - destinationCurrency = "eth" - destinationNetwork = "ethereum" - sourceExchangeAmount = "1" - supportedDestinationNetworks = []string{"ethereum", "polygon"} - resp, err = suite.client.CreateOnrampSession( - suite.ctx, - "redirect", - walletAddress, - sourceCurrency, - sourceExchangeAmount, - destinationNetwork, - destinationCurrency, - supportedDestinationNetworks, - ) - suite.Require().NoError(err, "should be able to create an onramp session with specific params") - suite.Require().NotEqual(resp.RedirectURL, "") -} diff --git a/libs/clients/stripe/instrumented_client.go b/libs/clients/stripe/instrumented_client.go deleted file mode 100644 index bfc3224a5..000000000 --- a/libs/clients/stripe/instrumented_client.go +++ /dev/null @@ -1,53 +0,0 @@ -package stripe - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "stripe_client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// CreateOnrampSession implements Client -func (_d ClientWithPrometheus) CreateOnrampSession(ctx context.Context, integrationMode string, walletAddress string, sourceCurrency string, sourceExchangeAmount string, destinationNetwork string, destinationCurrency string, supportedDestinationNetworks []string) (op1 *OnrampSessionResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateOnrampSession", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateOnrampSession(ctx, integrationMode, walletAddress, sourceCurrency, sourceExchangeAmount, destinationNetwork, destinationCurrency, supportedDestinationNetworks) -} diff --git a/libs/clients/stripe/mock/mock.go b/libs/clients/stripe/mock/mock.go deleted file mode 100644 index 997736114..000000000 --- a/libs/clients/stripe/mock/mock.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/stripe/client.go - -// Package mock_stripe is a generated GoMock package. -package mock_stripe - -import ( - context "context" - reflect "reflect" - - stripe "github.com/brave-intl/bat-go/libs/clients/stripe" - gomock "github.com/golang/mock/gomock" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// CreateOnrampSession mocks base method. -func (m *MockClient) CreateOnrampSession(ctx context.Context, integrationMode, walletAddress, sourceCurrency, sourceExchangeAmount, destinationNetwork, destinationCurrency string, supportedDestinationNetworks []string) (*stripe.OnrampSessionResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateOnrampSession", ctx, integrationMode, walletAddress, sourceCurrency, sourceExchangeAmount, destinationNetwork, destinationCurrency, supportedDestinationNetworks) - ret0, _ := ret[0].(*stripe.OnrampSessionResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateOnrampSession indicates an expected call of CreateOnrampSession. -func (mr *MockClientMockRecorder) CreateOnrampSession(ctx, integrationMode, walletAddress, sourceCurrency, sourceExchangeAmount, destinationNetwork, destinationCurrency, supportedDestinationNetworks interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOnrampSession", reflect.TypeOf((*MockClient)(nil).CreateOnrampSession), ctx, integrationMode, walletAddress, sourceCurrency, sourceExchangeAmount, destinationNetwork, destinationCurrency, supportedDestinationNetworks) -} diff --git a/libs/clients/zebpay/client.go b/libs/clients/zebpay/client.go deleted file mode 100644 index 6fca893d5..000000000 --- a/libs/clients/zebpay/client.go +++ /dev/null @@ -1,334 +0,0 @@ -package zebpay - -import ( - "bytes" - "context" - "crypto" - "crypto/sha256" - "errors" - "fmt" - "io/ioutil" - "net/http" - "os" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/requestutils" - jose "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" - "github.com/google/uuid" - "github.com/prometheus/client_golang/prometheus" - "github.com/shopspring/decimal" -) - -var ( - balanceGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "zebpay_account_balance", - Help: "A gauge of the current account balance in zebpay", - }) -) - -func init() { - prometheus.MustRegister(balanceGauge) -} - -// BulkTransfer is the structure of a bulk transfer for zebpay -type BulkTransferRequest []*Transfer - -/* -from the api specification -[ - { - "transaction_id": "c6911095-ba83-4aa1-b0fb-15934568a943", - "destination": 512, - "amount": "163", - "from": "c6911095-ba83-4aa1-b0fb-15934568a65a" - } -] -*/ - -// NewBulkTransferRequest returns a bulk transfer out of a list of transfers -func NewBulkTransferRequest(transfers ...*Transfer) BulkTransferRequest { - return BulkTransferRequest(transfers) -} - -// Transfer is the structure of a transfer for zebpay -type Transfer struct { - ID uuid.UUID `json:"transaction_id"` - Destination int64 `json:"destination"` - Amount decimal.Decimal `json:"amount"` - From uuid.UUID `json:"from"` -} - -// NewTransfer creates and returns a new transfer from parameters -func NewTransfer(id, from uuid.UUID, amount decimal.Decimal, destination int64) *Transfer { - return &Transfer{ - ID: id, From: from, Amount: amount, Destination: destination, - } -} - -// BulkTransferResponse is the response structure of the bulk transfer api call -type BulkTransferResponse struct { - Data string `json:"data"` -} - -/* -{ - "data": "ALL_SENT_TRANSACTIONS_ACKNOWLEDGED" -} -*/ - -// BulkCheckTransferResponse is the response structure from a bulk status check -type BulkCheckTransferResponse []*CheckTransferResponse - -// CheckTransferResponse is the structure of a transfer response -type CheckTransferResponse struct { - ID uuid.UUID `json:"transaction_id"` - Error string `json:"error"` - ErrorCode string `json:"error_code"` - Code int64 `json:"code"` - Status string `json:"status"` - Details Transfer `json:"details"` -} - -/* -{ - "transaction_id": "61e98be4-ea84-43d6-9b08-ef5cd95a55e2", - "code": 2, - "status": "Success", - "details": { - "amount": 13.736461457342187, - "destination": 60 - } -} -*/ - -var ( - CheckTransferCodeToStatus = map[int]string{ - TransferPendingCode: TransferPendingStatus, - TransferSuccessCode: TransferSuccessStatus, - TransferFailedCode: TransferFailedStatus, - } -) - -const ( - // TransferPendingCode is the status code for pending status - TransferPendingCode = 1 - // TransferSuccessCode is the status code for successful transfer - TransferSuccessCode = 2 - // TransferFailedCode is the status code for failed transfer - TransferFailedCode = 3 - // TransferNotFoundCode is the status code for transfer not found - TransferNotFoundCode = 404 - - // TransferPendingStatus is the status code for pending status - TransferPendingStatus = "Pending" - // TransferSuccessStatus is the status code for successful transfer - TransferSuccessStatus = "Success" - // TransferFailedStatus is the status code for failed transfer - TransferFailedStatus = "Failed" - // TransferNotFoundStatus is the status code for transfer not found - TransferNotFoundStatus = "NotFound" -) - -// ClientOpts are the common configuration options for the Zebpay Client -type ClientOpts struct { - APIKey string - SigningKey crypto.PrivateKey -} - -// Client abstracts over the underlying client -type Client interface { - // BulkTransfer posts a bulk transfer request to zebpay - BulkTransfer(ctx context.Context, opts *ClientOpts, req BulkTransferRequest) (*BulkTransferResponse, error) - // BulkCheckTransfer checks the status of a transaction - BulkCheckTransfer(ctx context.Context, opts *ClientOpts, ids ...uuid.UUID) (BulkCheckTransferResponse, error) - // CheckTransfer checks the status of a transaction - CheckTransfer(ctx context.Context, opts *ClientOpts, id uuid.UUID) (*CheckTransferResponse, error) -} - -// HTTPClient wraps http.Client for interacting with the cbr server -type HTTPClient struct { - client *clients.SimpleHTTPClient -} - -// New returns a new HTTPClient, retrieving the base URL from the environment -func New() (Client, error) { - serverEnvKey := "ZEBPAY_SERVER" - serverURL := os.Getenv(serverEnvKey) - if len(serverURL) == 0 { - return nil, errors.New(serverEnvKey + " was empty") - } - proxy := os.Getenv("HTTP_PROXY") - client, err := clients.NewWithProxy("zebpay", serverURL, "", proxy) // authentication bearer token is set per api call - if err != nil { - return nil, err - } - return NewClientWithPrometheus(&HTTPClient{client}, "zebpay_client"), err -} - -// New returns a new HTTPClient, retrieving the base URL from the environment -func NewWithHTTPClient(httpClient http.Client) (Client, error) { - serverEnvKey := "ZEBPAY_SERVER" - serverURL := os.Getenv(serverEnvKey) - if len(serverURL) == 0 { - return nil, errors.New(serverEnvKey + " was empty") - } - client, err := clients.NewWithHTTPClient(serverURL, "", &httpClient) // authentication bearer token is set per api call - if err != nil { - return nil, err - } - return NewClientWithPrometheus(&HTTPClient{client}, "zebpay_client"), err -} - -var errBadClientOpts = errors.New("client is misconfigured with no client options") - -type claims struct { - *jwt.Claims // use iat, exp, sub - same as x-api-key - URI string `json:"uri,omitempty"` // the endpoint called - Nonce uuid.UUID `json:"nonce,omitempty"` // one time use uuid - BodyHash string `json:"bodyHash,omitempty"` //Hex-encoded SHA-256 hash of the raw HTTP request body. -} - -// affixAccessToken will take a request and generate a zebpay access token and affix it to the -// headers of the request -func affixAccessToken(req *http.Request, opts *ClientOpts) error { - if opts == nil { - return errBadClientOpts - } - var body []byte - var err error - if req.Body != nil { - body, err = requestutils.Read(context.Background(), req.Body) - if err != nil { - return fmt.Errorf("failed to read request body: %w", err) - } - req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - } - - // sha body bytes - h := sha256.New() - h.Write(body) - shasum := fmt.Sprintf("%x", h.Sum(nil)) - - // add the api key to the request - req.Header.Set("X-API-KEY", opts.APIKey) - - // setup the signer options - key := jose.SigningKey{Algorithm: jose.RS256, Key: opts.SigningKey} - var signerOpts = jose.SignerOptions{} - signerOpts.WithType("JWT") - - // create a new signer with the signing key - rsaSigner, err := jose.NewSigner(key, &signerOpts) - if err != nil { - return fmt.Errorf("failed to create jwt signer: %w", err) - } - - // attach the rsa signer to the builder - builder := jwt.Signed(rsaSigner) - - // build out the claims of the token - builder = builder.Claims(claims{ - Claims: &jwt.Claims{ - Subject: opts.APIKey, - IssuedAt: jwt.NewNumericDate(time.Now()), - Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)), - }, - URI: req.URL.Path, - Nonce: uuid.New(), - BodyHash: shasum, - }) - accessToken, err := builder.CompactSerialize() - if err != nil { - return fmt.Errorf("failed to create jwt: %w", err) - } - - // add the authentication bearer token we created - req.Header.Set("Authorization", "Bearer "+accessToken) - - return nil -} - -// CheckTransfer uploads the bulk payout for gemini -func (c *HTTPClient) CheckTransfer(ctx context.Context, opts *ClientOpts, id uuid.UUID) (*CheckTransferResponse, error) { - resp := &CheckTransferResponse{ID: id} - if opts == nil { - resp.Error = errBadClientOpts.Error() - return resp, errBadClientOpts - } - - // /api/checktransferstatus//status - - urlPath := fmt.Sprintf("/api/checktransferstatus/%s/status", id.String()) - req, err := c.client.NewRequest(ctx, "GET", urlPath, nil, nil) - if err != nil { - resp.Error = err.Error() - return resp, err - } - - // populate the access token - affixAccessToken(req, opts) - - httpResponse, err := c.client.Do(ctx, req, resp) - if err != nil { - resp.Error = err.Error() - if httpResponse != nil { - resp.Code = int64(httpResponse.StatusCode) - } - } - - return resp, err -} - -// BulkCheckTransfer uploads the bulk payout for gemini -func (c *HTTPClient) BulkCheckTransfer(ctx context.Context, opts *ClientOpts, ids ...uuid.UUID) (BulkCheckTransferResponse, error) { - if opts == nil { - return nil, errBadClientOpts - } - - // /api/bulktransfercheckstatus - req, err := c.client.NewRequest(ctx, "GET", "/api/bulktransfercheckstatus", ids, nil) - if err != nil { - return nil, err - } - - // populate the access token - affixAccessToken(req, opts) - - var resp = BulkCheckTransferResponse{} - _, err = c.client.Do(ctx, req, &resp) - - return resp, err -} - -// BulkTransfer performs a bulk transfer for payouts -func (c *HTTPClient) BulkTransfer(ctx context.Context, opts *ClientOpts, transfers BulkTransferRequest) (*BulkTransferResponse, error) { - if opts == nil { - return nil, errBadClientOpts - } - - // check if any transfer exceeds our limit - for _, v := range transfers { - if v.Amount.GreaterThan(clients.TransferLimit) { - return nil, clients.ErrTransferExceedsLimit - } - } - - // /api/bulktransfercheckstatus - req, err := c.client.NewRequest(ctx, "POST", "/api/bulktransfer", transfers, nil) - if err != nil { - return nil, fmt.Errorf("failed to construct new request: %w", err) - } - - // populate the access token - affixAccessToken(req, opts) - - var resp = new(BulkTransferResponse) - _, err = c.client.Do(ctx, req, resp) - if err != nil { - return nil, fmt.Errorf("failed to do transfer request: %w", err) - } - - return resp, err -} diff --git a/libs/clients/zebpay/client_test.go b/libs/clients/zebpay/client_test.go deleted file mode 100644 index 837456af2..000000000 --- a/libs/clients/zebpay/client_test.go +++ /dev/null @@ -1,73 +0,0 @@ -//go:build integration && vpn -// +build integration,vpn - -package zebpay - -import ( - "context" - "crypto/x509" - "encoding/pem" - "os" - "strconv" - "testing" - - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/google/uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type ZebpayTestSuite struct { - suite.Suite - signingKey cryptography.PrivateKey - apikey string -} - -func TestZebpayTestSuite(t *testing.T) { - suite.Run(t, new(ZebpayTestSuite)) -} - -func (suite *ZebpayTestSuite) SetupTest() { - apiKey := os.Getenv("ZEBPAY_API_KEY") - signingKey := os.Getenv("ZEBPAY_SIGNING_KEY") - if signingKey != "" && apiKey != "" { - // parse the key from env variable - block, _ := pem.Decode([]byte(pemString)) - key, _ := x509.ParsePKCS1PrivateKey(block.Bytes) - // set the private key to secret - suite.signingKey = key - suite.apiKey = apiKey - } -} - -func (suite *ZebpayTestSuite) TestBulkTransfer() { - ctx := context.Background() - client, err := New() - suite.Require().NoError(err, "Must be able to correctly initialize the client") - five := decimal.NewFromFloat(5) - destination, err := strconv.ParseInt(os.Getenv("ZEBPAY_TEST_DESTINATION"), 10, 64) - suite.Require().NoError(err, "Must be able to get the test destination") - from := os.Getenv("ZEBPAY_TEST_FROM") - id := uuid.New() - opts := &ClientOpts{ - APIKey: suite.apiKey, - SigningKey: suite.signingKey, - } - - resp, err := client.BulkTransfer(ctx, opts, NewBulkTransferRequest(&transfer{ - ID: id, - Destination: destination, - Amount: five, - From: from, - })) - suite.Require().NoError(err, "Must be able to perform the bulk transfer") - // should have a success value in data - suite.Require().True(resp.Data == "ALL_SENT_TRANSACTIONS_ACKNOWLEDGED") - - // check on the transfer - status, err := client.CheckTransfer(ctx, opts, id) - suite.Require().NoError(err, "Must be able to perform the bulk transfer") - - // code should be pending or success, anything else is a failure - suite.Require().True(status.Code == TransferPendingCode || status.Code == TransferSuccessCode) -} diff --git a/libs/clients/zebpay/instrumented_client.go b/libs/clients/zebpay/instrumented_client.go deleted file mode 100644 index c6f92d62c..000000000 --- a/libs/clients/zebpay/instrumented_client.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -package zebpay - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/libs/clients/-i Client -t ../../../.prom-gowrap.tmpl -o instrumented_client.go -l "" - -import ( - "context" - "time" - - "github.com/google/uuid" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ClientWithPrometheus implements Client interface with all methods wrapped -// with Prometheus metrics -type ClientWithPrometheus struct { - base Client - instanceName string -} - -var clientDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "client_duration_seconds", - Help: "client runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewClientWithPrometheus returns an instance of the Client decorated with prometheus summary metric -func NewClientWithPrometheus(base Client, instanceName string) ClientWithPrometheus { - return ClientWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// BulkCheckTransfer implements Client -func (_d ClientWithPrometheus) BulkCheckTransfer(ctx context.Context, opts *ClientOpts, ids ...uuid.UUID) (b1 BulkCheckTransferResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "BulkCheckTransfer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BulkCheckTransfer(ctx, opts, ids...) -} - -// BulkTransfer implements Client -func (_d ClientWithPrometheus) BulkTransfer(ctx context.Context, opts *ClientOpts, req BulkTransferRequest) (bp1 *BulkTransferResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "BulkTransfer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BulkTransfer(ctx, opts, req) -} - -// CheckTransfer implements Client -func (_d ClientWithPrometheus) CheckTransfer(ctx context.Context, opts *ClientOpts, id uuid.UUID) (cp1 *CheckTransferResponse, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - clientDurationSummaryVec.WithLabelValues(_d.instanceName, "CheckTransfer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CheckTransfer(ctx, opts, id) -} diff --git a/libs/clients/zebpay/mock/mock.go b/libs/clients/zebpay/mock/mock.go deleted file mode 100644 index 74c7a3e2c..000000000 --- a/libs/clients/zebpay/mock/mock.go +++ /dev/null @@ -1,87 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./clients/zebpay/client.go - -// Package mock_zebpay is a generated GoMock package. -package mock_zebpay - -import ( - context "context" - reflect "reflect" - - zebpay "github.com/brave-intl/bat-go/libs/clients/zebpay" - gomock "github.com/golang/mock/gomock" - uuid "github.com/google/uuid" -) - -// MockClient is a mock of Client interface. -type MockClient struct { - ctrl *gomock.Controller - recorder *MockClientMockRecorder -} - -// MockClientMockRecorder is the mock recorder for MockClient. -type MockClientMockRecorder struct { - mock *MockClient -} - -// NewMockClient creates a new mock instance. -func NewMockClient(ctrl *gomock.Controller) *MockClient { - mock := &MockClient{ctrl: ctrl} - mock.recorder = &MockClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClient) EXPECT() *MockClientMockRecorder { - return m.recorder -} - -// BulkCheckTransfer mocks base method. -func (m *MockClient) BulkCheckTransfer(ctx context.Context, opts *zebpay.ClientOpts, ids ...uuid.UUID) (zebpay.BulkCheckTransferResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, opts} - for _, a := range ids { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "BulkCheckTransfer", varargs...) - ret0, _ := ret[0].(zebpay.BulkCheckTransferResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BulkCheckTransfer indicates an expected call of BulkCheckTransfer. -func (mr *MockClientMockRecorder) BulkCheckTransfer(ctx, opts interface{}, ids ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, opts}, ids...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BulkCheckTransfer", reflect.TypeOf((*MockClient)(nil).BulkCheckTransfer), varargs...) -} - -// BulkTransfer mocks base method. -func (m *MockClient) BulkTransfer(ctx context.Context, opts *zebpay.ClientOpts, req zebpay.BulkTransferRequest) (*zebpay.BulkTransferResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BulkTransfer", ctx, opts, req) - ret0, _ := ret[0].(*zebpay.BulkTransferResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BulkTransfer indicates an expected call of BulkTransfer. -func (mr *MockClientMockRecorder) BulkTransfer(ctx, opts, req interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BulkTransfer", reflect.TypeOf((*MockClient)(nil).BulkTransfer), ctx, opts, req) -} - -// CheckTransfer mocks base method. -func (m *MockClient) CheckTransfer(ctx context.Context, opts *zebpay.ClientOpts, id uuid.UUID) (*zebpay.CheckTransferResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckTransfer", ctx, opts, id) - ret0, _ := ret[0].(*zebpay.CheckTransferResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CheckTransfer indicates an expected call of CheckTransfer. -func (mr *MockClientMockRecorder) CheckTransfer(ctx, opts, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckTransfer", reflect.TypeOf((*MockClient)(nil).CheckTransfer), ctx, opts, id) -} diff --git a/libs/closers/log.go b/libs/closers/log.go deleted file mode 100644 index 0c60dad07..000000000 --- a/libs/closers/log.go +++ /dev/null @@ -1,18 +0,0 @@ -package closers - -import ( - "context" - "io" - - loggingutils "github.com/brave-intl/bat-go/libs/logging" -) - -// Log calls Close on the specified closer, panicing on error -func Log(ctx context.Context, c io.Closer) { - // get the logger from the context - logger := loggingutils.Logger(ctx, "closer.Log") - - if err := c.Close(); err != nil { - logger.Error().Err(err).Msg("error attempting to close") - } -} diff --git a/libs/closers/panic.go b/libs/closers/panic.go deleted file mode 100644 index 73176d146..000000000 --- a/libs/closers/panic.go +++ /dev/null @@ -1,29 +0,0 @@ -package closers - -import ( - "context" - "errors" - "io" - - "github.com/brave-intl/bat-go/libs/logging" -) - -// Panic calls Close on the specified closer, panicing on error -func Panic(ctx context.Context, c io.Closer) { - logger := logging.Logger(ctx, "closers.Panic") - if c == nil { - return - } - if err := c.Close(); err != nil { - logger.Error().Err(err).Msg("error attempting to close") - if errors.Is(err, context.Canceled) || err.Error() == "context canceled" { - // after this is merged we can remove this, the context timeout - // on the http client will manifest into this if the stream is not - // completed in time as "impact from not canceling the context is minor" - // https://go-review.googlesource.com/c/go/+/361919/ - // TODO: remove this when ^^ is released - return - } - panic(err.Error()) - } -} diff --git a/libs/concurrent/set.go b/libs/concurrent/set.go deleted file mode 100644 index 694808d13..000000000 --- a/libs/concurrent/set.go +++ /dev/null @@ -1,32 +0,0 @@ -package concurrent - -import "sync" - -// Set implements a concurrent set of keys -type Set struct { - m map[string]bool - sync.RWMutex -} - -// NewSet creates a new Set -func NewSet() *Set { - return &Set{ - m: make(map[string]bool), - } -} - -// Add the key to the set, returns true if the key did not already exist -func (s *Set) Add(k string) bool { - s.Lock() - defer s.Unlock() - _, exists := s.m[k] - s.m[k] = true - return !exists -} - -// Remove the key from the set -func (s *Set) Remove(k string) { - s.Lock() - defer s.Unlock() - delete(s.m, k) -} diff --git a/libs/concurrent/set_test.go b/libs/concurrent/set_test.go deleted file mode 100644 index fdabdea9b..000000000 --- a/libs/concurrent/set_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package concurrent - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSet(t *testing.T) { - k := "key" - s := NewSet() - // First insert returns true - assert.Equal(t, true, s.Add(k)) - // Subsequent insert(s) returns false - assert.Equal(t, false, s.Add(k)) - assert.Equal(t, false, s.Add(k)) - // We can delete a key from the set - s.Remove(k) - // First insert returns true - assert.Equal(t, true, s.Add(k)) -} diff --git a/libs/contains/contains.go b/libs/contains/contains.go deleted file mode 100644 index 87a79ac20..000000000 --- a/libs/contains/contains.go +++ /dev/null @@ -1,12 +0,0 @@ -package contains - -// Str (ing array) contains item -func Str(slice []string, item string) bool { - set := make(map[string]struct{}, len(slice)) - for _, s := range slice { - set[s] = struct{}{} - } - - _, ok := set[item] - return ok -} diff --git a/libs/context/getters.go b/libs/context/getters.go deleted file mode 100644 index c9cbe7adb..000000000 --- a/libs/context/getters.go +++ /dev/null @@ -1,91 +0,0 @@ -package context - -import ( - "context" - "fmt" - "time" - - "github.com/rs/zerolog" -) - -//GetByteSliceFromContext - given a CTXKey return the string value from the context if it exists -func GetByteSliceFromContext(ctx context.Context, key CTXKey) ([]byte, error) { - v := ctx.Value(key) - if v == nil { - // value not on context - return nil, ErrNotInContext - } - if s, ok := v.([]byte); ok { - return s, nil - } - // value not a string - return nil, ErrValueWrongType -} - -//GetBoolFromContext - given a CTXKey return the bool value from the context if it exists -func GetBoolFromContext(ctx context.Context, key CTXKey) (bool, error) { - v := ctx.Value(key) - if v == nil { - // value not on context - return false, ErrNotInContext - } - if s, ok := v.(bool); ok { - return s, nil - } - // value not a string - return false, ErrValueWrongType -} - -//GetStringFromContext - given a CTXKey return the string value from the context if it exists -func GetStringFromContext(ctx context.Context, key CTXKey) (string, error) { - v := ctx.Value(key) - if v == nil { - // value not on context - return "", ErrNotInContext - } - if s, ok := v.(string); ok { - return s, nil - } - // value not a string - return "", ErrValueWrongType -} - -//GetDurationFromContext - given a CTXKey return the duration value from the context if it exists -func GetDurationFromContext(ctx context.Context, key CTXKey) (time.Duration, error) { - v := ctx.Value(key) - if v == nil { - // value not on context - return time.Duration(0), ErrNotInContext - } - if s, ok := v.(time.Duration); ok { - return s, nil - } - // value not a duration - return time.Duration(0), ErrValueWrongType -} - -//GetLogLevelFromContext - given a CTXKey return the duration value from the context if it exists -func GetLogLevelFromContext(ctx context.Context, key CTXKey) (zerolog.Level, error) { - v := ctx.Value(key) - if v == nil { - // value not on context - return zerolog.InfoLevel, ErrNotInContext - } - if l, ok := v.(zerolog.Level); ok { - return l, nil - } - // value not a log level - return zerolog.InfoLevel, ErrValueWrongType -} - -//GetLogger - return the logger value from the context if it exists -func GetLogger(ctx context.Context) (*zerolog.Logger, error) { - // get the logger from the context, if the logger is disabled - // return an error to caller - var l = zerolog.Ctx(ctx) - if ll := *l; ll.GetLevel() == zerolog.Disabled { - // this is a disabled logger, send appropriate error - return nil, fmt.Errorf("logger not found in context: %w", ErrNotInContext) - } - return l, nil -} diff --git a/libs/context/keys.go b/libs/context/keys.go deleted file mode 100644 index d9b27ed26..000000000 --- a/libs/context/keys.go +++ /dev/null @@ -1,267 +0,0 @@ -package context - -import "errors" - -// CTXKey - a type for context keys -type CTXKey string - -const ( - // MergeCustodialCTXKey - the context key for merge custodial - MergeCustodialCTXKey CTXKey = "merge_custodial" - // AWSClientCTXKey - the context key for an aws client - AWSClientCTXKey CTXKey = "aws_client" - // DatastoreCTXKey - the context key for getting the datastore - DatastoreCTXKey CTXKey = "datastore" - // DatabaseTransactionCTXKey - context key for database transactions - DatabaseTransactionCTXKey CTXKey = "db_tx" - // RODatastoreCTXKey - the context key for getting the datastore - RODatastoreCTXKey CTXKey = "ro_datastore" - // PaginationOrderOptionsCTXKey - this is the pagination options context key - PaginationOrderOptionsCTXKey CTXKey = "pagination_order_options" - // ServiceKey - the key used for service context - ServiceKey CTXKey = "service" - // EnvironmentCTXKey - the key used for service context - EnvironmentCTXKey CTXKey = "environment" - // RatiosServerCTXKey - the context key for getting the ratios server - RatiosServerCTXKey CTXKey = "ratios_server" - // RatiosAccessTokenCTXKey - the context key for getting the ratios server access token - RatiosAccessTokenCTXKey CTXKey = "ratios_access_token" - // BaseCurrencyCTXKey - the context key for getting the default base currency - BaseCurrencyCTXKey CTXKey = "base_currency" - // DefaultMonthlyChoicesCTXKey - the context key for getting the default monthly choices - DefaultMonthlyChoicesCTXKey CTXKey = "default_monthly_choices" - // DefaultTipChoicesCTXKey - the context key for getting the default tip choices - DefaultTipChoicesCTXKey CTXKey = "default_tip_choices" - // DefaultACChoicesCTXKey - the context key for getting the default ac choices - DefaultACChoicesCTXKey CTXKey = "default_ac_choices" - // DefaultACChoiceCTXKey - the context key for getting the default ac choice - DefaultACChoiceCTXKey CTXKey = "default_ac_choice" - // RatiosCacheExpiryDurationCTXKey - context key for ratios client cache expiry - RatiosCacheExpiryDurationCTXKey CTXKey = "ratios_client_cache_expiry" - // RatiosCachePurgeDurationCTXKey - context key for ratios client cache purge - RatiosCachePurgeDurationCTXKey CTXKey = "ratios_client_cache_purge" - // DebugLoggingCTXKey - context key for debug logging - DebugLoggingCTXKey CTXKey = "debug_logging" - // ProgressLoggingCTXKey - context key for progress logging - ProgressLoggingCTXKey CTXKey = "progress_logging" - - // VersionCTXKey - context key for version of code - VersionCTXKey CTXKey = "version" - // CommitCTXKey - context key for the commit of the code - CommitCTXKey CTXKey = "commit" - // BuildTimeCTXKey - context key for the build time of code - BuildTimeCTXKey CTXKey = "build_time" - // ReputationClientCTXKey - context key for the build time of code - ReputationClientCTXKey CTXKey = "reputation_client" - // ZebPayLinkingKeyCTXKey - context key for the build time of code - ZebPayLinkingKeyCTXKey CTXKey = "zebpay_linking_key" - // DisableZebPayLinkingCTXKey - context key for the build time of code - DisableZebPayLinkingCTXKey CTXKey = "disable_zebpay_linking" - // GeminiClientCTXKey - context key for the build time of code - GeminiClientCTXKey CTXKey = "gemini_client" - // GeminiBrowserClientIDCTXKey - context key for the gemini browser client id - GeminiBrowserClientIDCTXKey CTXKey = "gemini_browser_client_id" - // GeminiClientIDCTXKey - context key for the gemini client id - GeminiClientIDCTXKey CTXKey = "gemini_client_id" - // GeminiClientSecretCTXKey - context key for the gemini client secret - GeminiClientSecretCTXKey CTXKey = "gemini_client_secret" - // GeminiAPIKeyCTXKey - context key for the gemini api key - GeminiAPIKeyCTXKey CTXKey = "gemini_api_key" - // GeminiAPISecretCTXKey - context key for the gemini api secret - GeminiAPISecretCTXKey CTXKey = "gemini_api_secret" - // GeminiSettlementAddressCTXKey - context key for the gemini settlement address - GeminiSettlementAddressCTXKey CTXKey = "gemini_settlement_address" - // GeminiServerURLCTXKey - context key for gemini server url - GeminiServerURLCTXKey CTXKey = "gemini_server_url" - // GeminiProxyURLCTXKey - context key for gemini proxy url - GeminiProxyURLCTXKey CTXKey = "gemini_proxy_url" - // GeminiTokenCTXKey - context key for gemini token - GeminiTokenCTXKey CTXKey = "gemini_token_url" - - // for skus ac validation - - // SkusGeminiClientCTXKey - context key for the build time of code - SkusGeminiClientCTXKey CTXKey = "skus_gemini_client" - // SkusGeminiBrowserClientIDCTXKey - context key for the gemini browser client id - SkusGeminiBrowserClientIDCTXKey CTXKey = "skus_gemini_browser_client_id" - // SkusGeminiClientIDCTXKey - context key for the gemini client id - SkusGeminiClientIDCTXKey CTXKey = "skus_gemini_client_id" - // SkusGeminiClientSecretCTXKey - context key for the gemini client secret - SkusGeminiClientSecretCTXKey CTXKey = "skus_gemini_client_secret" - // SkusGeminiAPIKeyCTXKey - context key for the gemini api key - SkusGeminiAPIKeyCTXKey CTXKey = "skus_gemini_api_key" - // SkusGeminiAPISecretCTXKey - context key for the gemini api secret - SkusGeminiAPISecretCTXKey CTXKey = "skus_gemini_api_secret" - // SkusGeminiSettlementAddressCTXKey - context key for the gemini settlement address - SkusGeminiSettlementAddressCTXKey CTXKey = "skus_gemini_settlement_address" - // SkusEnableStoreSignedOrderCredsConsumer enables the store sigend order creds consumers - SkusEnableStoreSignedOrderCredsConsumer CTXKey = "skus_enable_store_signed_order_creds_consumer" - // SkusNumberStoreSignedOrderCredsConsumer number of consumers to create for store signed order creds - SkusNumberStoreSignedOrderCredsConsumer CTXKey = "skus_number_store_signed_order_creds_consumer" - - // Kafka509CertCTXKey - context key for the build time of code - Kafka509CertCTXKey CTXKey = "kafka_x509_cert" - // KafkaBrokersCTXKey - context key for the build time of code - KafkaBrokersCTXKey CTXKey = "kafka_brokers" - // BraveTransferPromotionIDCTXKey - context key for the build time of code - BraveTransferPromotionIDCTXKey CTXKey = "brave_transfer_promotion_id" - // WalletOnPlatformPriorToCTXKey - context key for the build time of code - WalletOnPlatformPriorToCTXKey CTXKey = "wallet_on_platform_prior_to" - // LogLevelCTXKey - context key for application logging level - LogLevelCTXKey CTXKey = "log_level" - - // Bitflyer Keys - - // BitFlyerJWTKeyCTXKey - context key for the bitflyer jwt key - BitFlyerJWTKeyCTXKey CTXKey = "bitflyer_jwt_key" - // BitflyerExtraClientSecretCTXKey - context key for the extra client secret - BitflyerExtraClientSecretCTXKey CTXKey = "bitflyer_extra_client_secret" - // BitflyerClientSecretCTXKey - context key for the client secret - BitflyerClientSecretCTXKey CTXKey = "bitflyer_client_secret" - // BitflyerClientIDCTXKey - context key for the client secret - BitflyerClientIDCTXKey CTXKey = "bitflyer_client_id" - // BitflyerServerURLCTXKey - the service ctx key - BitflyerServerURLCTXKey CTXKey = "bitflyer_server" - // BitflyerProxyURLCTXKey - the service proxy ctx key - BitflyerProxyURLCTXKey CTXKey = "bitflyer_proxy" - // BitflyerTokenCTXKey - the service token ctx key - BitflyerTokenCTXKey CTXKey = "bitflyer_token" - // BitflyerSourceFromCTXKey - the source from for payouts - BitflyerSourceFromCTXKey CTXKey = "bitflyer_source_from" - - // ReputationOnDrainCTXKey - context key for getting the reputation on drain feature flag - ReputationOnDrainCTXKey CTXKey = "reputation_on_drain" - // UseCustodianRegionsCTXKey - context key for getting the reputation on drain feature flag - UseCustodianRegionsCTXKey CTXKey = "use-custodian-regions" - // CustodianRegionsCTXKey - context key for getting the reputation on drain feature flag - CustodianRegionsCTXKey CTXKey = "custodian-regions" - // ReputationWithdrawalOnDrainCTXKey - context key for getting the reputation on drain feature flag - ReputationWithdrawalOnDrainCTXKey CTXKey = "reputation_withdrawal_on_drain" - // SkipRedeemCredentialsCTXKey - context key for getting the skip redeem credentials - SkipRedeemCredentialsCTXKey CTXKey = "skip_redeem_credentials" - - // DisableUpholdLinkingCTXKey - this informs if uphold linking is enabled - DisableUpholdLinkingCTXKey CTXKey = "disable_uphold_linking" - // DisableGeminiLinkingCTXKey - this informs if gemini linking is enabled - DisableGeminiLinkingCTXKey CTXKey = "disable_gemini_linking" - // DisableBitflyerLinkingCTXKey - this informs if bitflyer linking is enabled - DisableBitflyerLinkingCTXKey CTXKey = "disable_bitflyer_linking" - - // RadomWebhookSecretCTXKey - the webhook secret key for radom integration - RadomWebhookSecretCTXKey CTXKey = "radom_webhook_secret" - // RadomEnabledCTXKey - this informs if radom is enabled - RadomEnabledCTXKey CTXKey = "radom_enabled" - // RadomSellerAddressCTXKey is the seller address on radom - RadomSellerAddressCTXKey CTXKey = "radom_seller_address" - // RadomServerCTXKey is the server address on radom - RadomServerCTXKey CTXKey = "radom_server" - // RadomSecretCTXKey is the server secret on radom - RadomSecretCTXKey CTXKey = "radom_secret" - - // stripe related keys - - // StripeEnabledCTXKey - this informs if stripe is enabled - StripeEnabledCTXKey CTXKey = "stripe_enabled" - // StripeWebhookSecretCTXKey - the webhook secret key for stripe integration - StripeWebhookSecretCTXKey CTXKey = "stripe_webhook_secret" - // StripeSecretCTXKey - the secret key for stripe integration - StripeSecretCTXKey CTXKey = "stripe_secret" - // WhitelistSKUsCTXKey - context key for whitelisted skus - WhitelistSKUsCTXKey CTXKey = "whitelist_skus" - - // RateLimiterBurstCTXKey - context key for allowing a bursting rate limiter - RateLimiterBurstCTXKey CTXKey = "rate_limit_burst" - // NoUnlinkPriorToDurationCTXKey - the iso duration of time that no unlinking must have happened - NoUnlinkPriorToDurationCTXKey CTXKey = "no_unlinkings_prior_to" - // CoingeckoServerCTXKey - the context key for getting the coingecko server - CoingeckoServerCTXKey CTXKey = "coingecko_server" - // CoingeckoAccessTokenCTXKey - the context key for getting the coingecko server access token - CoingeckoAccessTokenCTXKey CTXKey = "coingecko_access_token" - - // CoingeckoIDToSymbolCTXKey - the context key for getting the mapping from coin id to symbol - CoingeckoIDToSymbolCTXKey CTXKey = "coingecko_id_to_symbol" - // CoingeckoSymbolToIDCTXKey - the context key for getting the mapping from coin symbol to id - CoingeckoSymbolToIDCTXKey CTXKey = "coingecko_symbol_to_id" - // CoingeckoContractToIDCTXKey - the context key for getting the mapping from coin contract to id - CoingeckoContractToIDCTXKey CTXKey = "coingecko_contract_to_id" - // CoingeckoSupportedVsCurrenciesCTXKey - the context key for getting the list of supported vs currencies - CoingeckoSupportedVsCurrenciesCTXKey CTXKey = "coingecko_supported_vs_currencies" - // CoingeckoCoinLimitCTXKey - the context key for getting the max number of coins - CoingeckoCoinLimitCTXKey CTXKey = "coingecko_coin_limit" - // CoingeckoVsCurrencyLimitCTXKey - the context key for getting the max number of vs currencies - CoingeckoVsCurrencyLimitCTXKey CTXKey = "coingecko_vs_currency_limit" - // RatiosRedisAddrCTXKey - the context key for getting the ratios redis address - RatiosRedisAddrCTXKey CTXKey = "ratios_redis_addr" - // BlacklistedCountryCodesCTXKey - the context key for getting the ratios redis address - BlacklistedCountryCodesCTXKey CTXKey = "blacklisted_country_codes" - // RateLimitPerMinuteCTXKey - the context key for getting the rate limit - RateLimitPerMinuteCTXKey CTXKey = "rate_limit_per_min" - // SecretsURICTXKey - the context key for getting the application secrets file location - SecretsURICTXKey CTXKey = "secrets_uri" - // ParametersMergeBucketCTXKey - the context key for getting the rate limit - ParametersMergeBucketCTXKey CTXKey = "merge_param_bucket" - - // RequireUpholdCountryCTXKey - the context key for getting the rate limit - RequireUpholdCountryCTXKey CTXKey = "require_uphold_country" - - // DisabledWalletGeoCountriesCTXKey context key used to retrieve the S3 object name for disabled wallet geo countries - DisabledWalletGeoCountriesCTXKey CTXKey = "disabled_wallet_geo_countries" - - // PlaystoreJSONKeyCTXKey - the context key for playstore json key - PlaystoreJSONKeyCTXKey CTXKey = "playstore_json_key" - - // AppleReceiptSharedKeyCTXKey - the context key for appstore key - AppleReceiptSharedKeyCTXKey CTXKey = "apple_receipt_shared_key" - - // DisableDisconnectCTXKey - the context key for rewards wallet disconnect capability key - DisableDisconnectCTXKey CTXKey = "disable_disconnect" - - // ParametersVBATDeadlineCTXKey - the context key for getting the vbat deadline - ParametersVBATDeadlineCTXKey CTXKey = "parameters_vbat_deadline" - // ParametersTransitionCTXKey - the context key for getting the vbat deadline - ParametersTransitionCTXKey CTXKey = "parameters_transition" - - // StripeAccessTokenCTXKey - the context key for the Stripe secret key - StripeOnrampSecretKeyCTXKey CTXKey = "stripe_onramp_secret_key" - - // StripeServerCTXKey - the context key for the Stripe server - StripeOnrampServerCTXKey CTXKey = "stripe_onramp_server" - - // Nitro - // PaymentsEncryptionKeyCTXKey - the context key for getting the application secrets file location - PaymentsEncryptionKeyCTXKey CTXKey = "payments_encryption_key" - // PaymentsSenderPublicKeyCTXKey - the context key for getting the application secrets file location - PaymentsSenderPublicKeyCTXKey CTXKey = "payments_sender_public_key" - // PaymentsKMSWrapperCTXKey - the context key for getting the kms wrapper key - PaymentsKMSWrapperCTXKey CTXKey = "payments_kms_wrapper" - // AWSRegionCTXKey - AWS region for SDK integration - AWSRegionCTXKey CTXKey = "aws_region" - // PaymentsQLDBRoleArnCTXKey - ARN of the AWS role to assume for QLDB access - PaymentsQLDBRoleArnCTXKey CTXKey = "qldb_role_arn" - // PaymentsQLDBLedgerNameCTXKey - Name of the QLDB ledger to which the role has access - PaymentsQLDBLedgerNameCTXKey CTXKey = "qldb_ledger_name" - // PaymentsQLDBLedgerARNCTXKey - ARN of the AWS QLDB ledger to which the role has access - PaymentsQLDBLedgerARNCTXKey CTXKey = "qldb_ledger_arn" - - // LogWriterCTXKey - the context key for getting the log writer - LogWriterCTXKey CTXKey = "log_writer" - // EgressProxyAddrCTXKey - the context key for getting the egress proxy address - EgressProxyAddrCTXKey CTXKey = "egress_proxy_addr" - // EnclaveDecryptKeyTemplateSecretIDCTXKey - the context key for getting the key template for key creation - EnclaveDecryptKeyTemplateSecretIDCTXKey CTXKey = "enclave_decrypt_key_template_secret" - // EnclaveConfigObjectNameCTXKey specifies the config object name for nitro enclave payments service - EnclaveSecretsObjectNameCTXKey CTXKey = "enclave_config_object_name" - // EnclaveSolanaAddressCTXKey specifies the solana address for nitro enclave payments service - EnclaveSolanaAddressCTXKey CTXKey = "enclave_solana_address_name" - // EnclaveConfigBucketNameCTXKey specifies the config bucket name for nitro enclave payments service - EnclaveSecretsBucketNameCTXKey CTXKey = "enclave_config_bucket_name" - // EnclaveOperatorSharesBucketNameCTXKey specifies the operator shares bucket name for nitro enclave payments service - EnclaveOperatorSharesBucketNameCTXKey CTXKey = "enclave_operator_shares_bucket_name" -) - -var ( - // ErrNotInContext - error you get when you ask for something not in the context. - ErrNotInContext = errors.New("failed to get value from context") - // ErrValueWrongType - error you get when you ask for something, and it is not the type you expected - ErrValueWrongType = errors.New("context value of wrong type") -) diff --git a/libs/context/wrap.go b/libs/context/wrap.go deleted file mode 100644 index c92c57a70..000000000 --- a/libs/context/wrap.go +++ /dev/null @@ -1,38 +0,0 @@ -package context - -import ( - "context" -) - -// wrapper allows for wrapping the values of a context with the cancellation of a new one -// approach from https://github.com/posener/ctxutil -type wrapper struct { - wrapped context.Context - context.Context -} - -// Value returns the value associated with this context for key, or nil -// if no value is associated with key. Successive calls to Value with -// the same key returns the same result. -func (w *wrapper) Value(k interface{}) interface{} { - if v := w.Context.Value(k); v != nil { - return v - } - return w.wrapped.Value(k) -} - -// Wrap a context, inheriting the values of the wrapped context -// nolint:golint -func Wrap(wrapped context.Context, context context.Context) context.Context { - return &wrapper{wrapped, context} -} - -// MapToContext - import a map to the context -func MapToContext(ctx context.Context, m map[CTXKey]interface{}) context.Context { - for k, v := range m { - if vv := ctx.Value(k); vv == nil { - ctx = context.WithValue(ctx, k, v) - } - } - return ctx -} diff --git a/libs/cryptography/attenutated_credential.go b/libs/cryptography/attenutated_credential.go deleted file mode 100644 index aa0d213dd..000000000 --- a/libs/cryptography/attenutated_credential.go +++ /dev/null @@ -1,93 +0,0 @@ -package cryptography - -import ( - "bytes" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "errors" - "strings" - - "golang.org/x/crypto/hkdf" -) - -// SecretTokenPrefix all secret keys will be contain -const SecretTokenPrefix = "secret-token:" - -// DecodeKeyID into the root keyID and any caveats, which will be null otherwise -func DecodeKeyID(keyID string) (rootKeyID string, caveats map[string]string, err error) { - s := strings.Split(keyID, ":") - if len(s) != 1 && len(s) != 2 { - err = errors.New("invalid keyID") - return - } - - rootKeyID = s[0] - if len(s) == 2 { - var caveatBytes []byte - var verifyCaveatBytes []byte - caveatBytes, err = base64.URLEncoding.DecodeString(s[1]) - if err != nil { - return - } - err = json.Unmarshal(caveatBytes, &caveats) - if err != nil { - return - } - - // Verify canonical encoding by re-marshalling - verifyCaveatBytes, err = json.Marshal(caveats) - if err != nil { - return - } - if !bytes.Equal(caveatBytes, verifyCaveatBytes) { - err = errors.New("caveats do not match canonical encoding") - return - } - - return - } - caveats = nil - return -} - -// Attenuate a root keyID and secretKey usign the provided caveats -func Attenuate(rootKeyID string, secretKey string, caveats map[string]string) (aKeyID string, aSecretKey string, err error) { - if len(caveats) == 0 { - err = errors.New("caveats cannot be nil or empty") - return - } - _, c, err := DecodeKeyID(rootKeyID) - if err != nil { - return - } - if c != nil { - err = errors.New("key has already been attenutated") - return - } - - if !strings.HasPrefix(secretKey, SecretTokenPrefix) { - err = errors.New("invalid secretKey") - return - } - - caveatBytes, err := json.Marshal(caveats) - if err != nil { - return - } - km := hkdf.New(sha256.New, []byte(secretKey), nil, caveatBytes) - - key := make([]byte, len(secretKey)) - - n, err := km.Read(key) - if err != nil { - return - } - if n != len(secretKey) { - err = errors.New("failed to generate attenuated key") - return - } - aKeyID = rootKeyID + ":" + base64.URLEncoding.EncodeToString(caveatBytes) - aSecretKey = SecretTokenPrefix + base64.RawURLEncoding.EncodeToString(key) - return -} diff --git a/libs/cryptography/attenutated_credential.md b/libs/cryptography/attenutated_credential.md deleted file mode 100644 index 882520a06..000000000 --- a/libs/cryptography/attenutated_credential.md +++ /dev/null @@ -1,41 +0,0 @@ -# Creation of Merchant Attenuated Credentials - -Below is the process for creation of merchant credentials for merchant HTTP Signatures, -primarily useful for the SKUs credential validation endpoint. - -1. [ ] create merchant shared key -``` - b := make([]byte, 24) - rand.Read(b) - secretPlaintext := "secret-token:" + base64.RawURLEncoding.EncodeToString(b) -``` -2. [ ] encrypt merchant shared key -``` - var byteEncryptionKey [32]byte - copy(byteEncryptionKey[:], []byte(os.Getenv("ENCRYPTION_KEY"))) // the encryption key set for the environment - encryptedBytes, nonce, err := EncryptMessage(byteEncryptionKey, []byte(secretPlaintext)) -``` -3. [ ] upload merchant encrypted shared key and secret to the SKUs database `api_keys` table -``` - insert into api_keys (name, merchant_id, encrypted_secret_key, nonce) values - ( - 'Name of Merchant Key', 'Merchant ID (i.e. "brave.com"), - string(encryptedBytes), string(nonce) - ); - -- save key id created for next step -``` -4. [ ] attenuate merchant key -``` - keyID := // the id from the db insert to the api_keys table - secretKey := secretPlaintext // from the first step, the generated random secret - - caveats := map[string]string { - "merchant": "brave.com", // brave.com in this case is the "merchant_id" in the db from prior step - } - - keyID, secretKey, err := Attenuate(keyID, secretKey, caveats) -``` -5. [ ] securely deliver attenuated keyID/keySecret to merchant -``` - The caller will need keyID and secretKey from the last step, deliver them securely -``` diff --git a/libs/cryptography/attenutated_credential_test.go b/libs/cryptography/attenutated_credential_test.go deleted file mode 100644 index e98668554..000000000 --- a/libs/cryptography/attenutated_credential_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package cryptography - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMarshalCaveats(t *testing.T) { - // Ensure map[string]string marshal follows canonical encoding for caveats - expectedBytes := []byte("{\"a\":\"1\",\"b\":\"1\"}") - - // Independant of initialization order - caveatBytes, err := json.Marshal( - map[string]string{ - "b": "1", - "a": "1", - }) - assert.NoError(t, err, "marshal should not fail") - assert.Equal(t, caveatBytes, expectedBytes, "marshaled caveats should be equal") - - caveatBytes, err = json.Marshal( - map[string]string{ - "a": "1", - "b": "1", - }) - assert.NoError(t, err, "marshal should not fail") - assert.Equal(t, caveatBytes, expectedBytes, "marshaled caveats should be equal") - - // Independant of insert order - caveats := map[string]string{} - caveats["b"] = "1" - caveats["a"] = "1" - caveatBytes, err = json.Marshal(caveats) - assert.NoError(t, err, "marshal should not fail") - assert.Equal(t, caveatBytes, expectedBytes, "marshaled caveats should be equal") - - caveats = map[string]string{} - caveats["a"] = "1" - caveats["b"] = "1" - caveatBytes, err = json.Marshal(caveats) - assert.NoError(t, err, "marshal should not fail") - assert.Equal(t, caveatBytes, expectedBytes, "marshaled caveats should be equal") -} - -func TestAttenuate(t *testing.T) { - keyID := "foo" - secretKey := "secret-token:bar" - - caveats := map[string]string{ - "merchant": "brave.com", - } - - aKeyID, aSecretKey, err := Attenuate(keyID, secretKey, caveats) - assert.NoError(t, err, "attenutate should succeed") - - _, _, err = Attenuate(aKeyID, aSecretKey, caveats) - assert.Error(t, err, "must not be able to attenuate an already attenuated key") -} - -func TestDecodeKeyID(t *testing.T) { - aKeyID := "foo:eyJtZXJjaGFudCI6ImJyYXZlLmNvbSJ9" - - root, caveats, err := DecodeKeyID(aKeyID) - assert.NoError(t, err, "decode should succeed") - - assert.Equal(t, root, "foo") - assert.Equal(t, caveats, map[string]string{"merchant": "brave.com"}) -} diff --git a/libs/cryptography/encryption.go b/libs/cryptography/encryption.go deleted file mode 100644 index 03285b11b..000000000 --- a/libs/cryptography/encryption.go +++ /dev/null @@ -1,56 +0,0 @@ -package cryptography - -import ( - "crypto/rand" - "errors" - "io" - - "golang.org/x/crypto/nacl/secretbox" -) - -// Both what the encryption key length should be -var keyLength = 32 - -// 4KB is a reasonable chunk size. -const secretBoxMaxChunkSize = 4000 - -var ( - // ErrEncryptedFieldTooLarge - the sku was invalid - ErrEncryptedFieldTooLarge = errors.New("encrypted field is greater than 4 KB - this must be chunked") -) - -// EncryptMessage uses SecretBox to encrypt the message -func EncryptMessage(encryptionKey [32]byte, field []byte) (encrypted []byte, nonceString [24]byte, err error) { - var nonce [24]byte - - // The key argument should be 32 bytes long - if len(encryptionKey) != keyLength { - return nil, nonce, errors.New("encryption Key is not the correct key length") - } - - // large amounts of data should be chunked - if len(field) >= secretBoxMaxChunkSize { - return nil, nonce, ErrEncryptedFieldTooLarge - } - - if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { - return nil, nonce, err - } - - out := make([]byte, 0, secretbox.Overhead+len(field)) - encryptedField := secretbox.Seal(out[:], field, &nonce, &encryptionKey) - - return encryptedField, nonce, nil -} - -// DecryptMessage uses SecretBox to decrypt the message -func DecryptMessage(encryptionKey [32]byte, encryptedField []byte, nonce []byte) (string, error) { - var decryptNonce [24]byte - copy(decryptNonce[:], nonce) - decrypted, ok := secretbox.Open(nil, encryptedField, &decryptNonce, &encryptionKey) - if !ok { - return "", errors.New("could not decrypt the value of the secret") - } - - return string(decrypted), nil -} diff --git a/libs/cryptography/encryption_test.go b/libs/cryptography/encryption_test.go deleted file mode 100644 index e5c0a54bf..000000000 --- a/libs/cryptography/encryption_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package cryptography - -import ( - "encoding/hex" - "errors" - "fmt" - "testing" -) - -func TestEncryptionMessage(t *testing.T) { - // set up the aes key, typically done with env variable atm - var byteEncryptionKey [32]byte - copy(byteEncryptionKey[:], []byte("MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0")) - - tooLarge := make([]byte, 16001) - - _, _, err := EncryptMessage(byteEncryptionKey, tooLarge) - if !errors.Is(err, ErrEncryptedFieldTooLarge) { - t.Error("Encrypted field failed validations", err) - } - - encryptedBytes, n, err := EncryptMessage(byteEncryptionKey, []byte("Hello World!")) - if err != nil { - t.Error("error while running encrypt message", err) - } - - encrypted, err := hex.DecodeString(fmt.Sprintf("%x", encryptedBytes)) - if err != nil { - t.Error("error while decoding the encrypted string", err) - } - nonce, err := hex.DecodeString(fmt.Sprintf("%x", n)) - if err != nil { - t.Error("error while decoding the nonce", err) - } - - if len(nonce) != 24 { - t.Error("Nonce does not have correct length", err) - } - - secretKey, err := DecryptMessage(byteEncryptionKey, encrypted, nonce) - if err != nil { - t.Error("error in decrypt secret: ", err) - } - - if secretKey != "Hello World!" { - t.Error("Encryption and decryption did not work") - } -} diff --git a/libs/cryptography/hmac.go b/libs/cryptography/hmac.go deleted file mode 100644 index c19f8f09b..000000000 --- a/libs/cryptography/hmac.go +++ /dev/null @@ -1,37 +0,0 @@ -package cryptography - -import ( - "crypto/hmac" - "crypto/sha512" - "errors" -) - -// HMACKey an interface for hashing to hmac-sha384 -type HMACKey interface { - // HMACSha384 does the appropriate hashing - HMACSha384(payload []byte) ([]byte, error) -} - -// HMACHasher is an in process signer implementation for HMACKey -type HMACHasher struct { - secret []byte -} - -// NewHMACHasher creates a new HMACKey for hashing -func NewHMACHasher(secret []byte) HMACKey { - hasher := HMACHasher{secret} - return &hasher -} - -// HMACSha384 hashes using an in process secret -func (hmh *HMACHasher) HMACSha384(payload []byte) ([]byte, error) { - mac := hmac.New(sha512.New384, hmh.secret) - len, err := mac.Write([]byte(payload)) - if err != nil { - return []byte{}, err - } - if len == 0 { - return []byte{}, errors.New("no bytes written in HMACSha384 Hash") - } - return mac.Sum(nil), nil -} diff --git a/libs/cryptography/presigner.go b/libs/cryptography/presigner.go deleted file mode 100644 index c2ede6482..000000000 --- a/libs/cryptography/presigner.go +++ /dev/null @@ -1,21 +0,0 @@ -package cryptography - -// Presignable duplicates the hmac interface for signing -type Presignable interface { - HMACSha384(payload []byte) ([]byte, error) -} - -// Presigner returns the same value always -type Presigner struct { - sig []byte -} - -// HMACSha384 presigns a request -func (ps Presigner) HMACSha384(payload []byte) ([]byte, error) { - return ps.sig, nil -} - -// NewPresigner creates a new presigner -func NewPresigner(sig []byte) Presignable { - return Presigner{sig} -} diff --git a/libs/cryptography/time_limited.go b/libs/cryptography/time_limited.go deleted file mode 100644 index d1a636f94..000000000 --- a/libs/cryptography/time_limited.go +++ /dev/null @@ -1,39 +0,0 @@ -package cryptography - -import ( - "crypto/subtle" - "encoding/base64" - "time" -) - -// TimeLimitedSecret represents a secret used to derive Time Limited Credentials -type TimeLimitedSecret struct { - hasher HMACKey -} - -// Derive - derive time limited credential based on date and expiration date -func (secret TimeLimitedSecret) Derive(metadata []byte, date time.Time, expirationDate time.Time) (string, error) { - interval := date.Format("2006-01-02") + "/" + expirationDate.Format("2006-01-02") - - result, err := secret.hasher.HMACSha384(append(metadata, []byte(interval)...)) - if err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(result), nil -} - -// Verify - verify time limited credential based on date being bound within the expiration date of the credential -func (secret TimeLimitedSecret) Verify(metadata []byte, date time.Time, expirationDate time.Time, token string) (bool, error) { - result, err := secret.Derive(metadata, date, expirationDate) - if err != nil { - return false, err - } - return subtle.ConstantTimeCompare([]byte(result), []byte(token)) == 1, nil -} - -// NewTimeLimitedSecret - create a new time limited secret structure -func NewTimeLimitedSecret(secret []byte) TimeLimitedSecret { - return TimeLimitedSecret{ - hasher: NewHMACHasher(secret), - } -} diff --git a/libs/cryptography/time_limited_test.go b/libs/cryptography/time_limited_test.go deleted file mode 100644 index b5d602de9..000000000 --- a/libs/cryptography/time_limited_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package cryptography - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestTimeLimitedCredential(t *testing.T) { - secret := "placeholder" - metadata := "test" - start := time.Date(2006, time.January, 2, 0, 0, 0, 0, time.UTC) - end := time.Date(2006, time.January, 3, 0, 0, 0, 0, time.UTC) - - timeLimitedSecret := NewTimeLimitedSecret([]byte(secret)) - result, err := timeLimitedSecret.Derive([]byte(metadata), start, end) - assert.NoError(t, err) - - if result != "/OXS+mLpp6NrDWV3dKOMBHX8lOoCnavWXvkSjPK6yye0JQwomvzoLsKqUwSB4Oya" { - t.Error("failed to match") - } -} - -func TestTimeLimitedCredentialVerification(t *testing.T) { - - timeLimitedSecret := NewTimeLimitedSecret([]byte("1cdd2af5-1c5a-47f6-bdad-89090acee31c")) - - verifyTime, err := time.Parse("2006-01-02", "2020-12-23") - assert.NoError(t, err) - expirationTime, err := time.Parse("2006-01-02", "2020-12-30") - assert.NoError(t, err) - metadata := []byte("metadata") - - result, err := timeLimitedSecret.Derive(metadata, verifyTime, expirationTime) - assert.NoError(t, err, "Error deriving token") - - correctVerifyTime, err := time.Parse("2006-01-02", "2020-12-23") - assert.NoError(t, err) - incorrectVerifyTime, err := time.Parse("2006-01-02", "2020-12-24") - assert.NoError(t, err) - - correctlyVerified, err := timeLimitedSecret.Verify(metadata, correctVerifyTime, expirationTime, result) - assert.NoError(t, err, "Error verifying token") - assert.True(t, correctlyVerified, "Error verifying with correct verify time") - - incorrectlyVerified, err := timeLimitedSecret.Verify(metadata, incorrectVerifyTime, expirationTime, result) - assert.NoError(t, err, "Error verifying token") - assert.False(t, incorrectlyVerified, "Token should not have verified with incorrect verify time") - - incorrectMetadataVerified, err := timeLimitedSecret.Verify([]byte("not metadata"), correctVerifyTime, expirationTime, result) - assert.NoError(t, err, "Error verifying token") - assert.False(t, incorrectMetadataVerified, "Error verifying with invalid metadata") -} diff --git a/libs/custodian/provider/bitflyer.go b/libs/custodian/provider/bitflyer.go deleted file mode 100644 index 170a9f96e..000000000 --- a/libs/custodian/provider/bitflyer.go +++ /dev/null @@ -1,319 +0,0 @@ -package provider - -import ( - "context" - "errors" - "fmt" - "net/http" - "sort" - "strings" - - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/bitflyer" - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - loggingutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/gofrs/uuid" - "github.com/shopspring/decimal" -) - -var ( - // BitflyerTransferIDNamespace uuidv5 namespace for bitflyer transfer id creation - BitflyerTransferIDNamespace = uuid.Must(uuid.FromString("5b208c1d-e1c4-4799-bcc2-0b08b9c660f5")) - // ErrInvalidSource - invalid source for bitflyer - ErrInvalidSource = errors.New("invalid source for bitflyer") -) - -// bitflyerCustodian - implementation of the bitflyer custodian -type bitflyerCustodian struct { - client bitflyer.Client -} - -// newBitflyerCustodian - create a new bitflyer custodian with configuration -func newBitflyerCustodian(ctx context.Context, conf Config) (*bitflyerCustodian, error) { - logger := loggingutils.Logger(ctx, "custodian.newBitflyerCustodian").With().Str("conf", conf.String()).Logger() - - // import config to context if not already set, and create bitflyer client - bfClient, err := bitflyer.NewWithContext(appctx.MapToContext(ctx, conf.Config)) - if err != nil { - msg := "failed to create client" - return nil, loggingutils.LogAndError(&logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - return &bitflyerCustodian{ - client: bfClient, - }, nil -} - -func isBitflyerErrorUnauthorized(ctx context.Context, err error) bool { - var bfe *clients.BitflyerError - if errors.As(err, &bfe) { - return bfe.HTTPStatusCode == http.StatusUnauthorized - } - var bundleErr *errorutils.ErrorBundle - if errors.As(err, &bundleErr) { - if state, ok := bundleErr.Data().(clients.HTTPState); ok { - return state.Status == http.StatusUnauthorized - } - } - return false -} - -// SubmitTransactions - implement Custodian interface -func (bc *bitflyerCustodian) SubmitTransactions(ctx context.Context, txs ...Transaction) error { - // setup logger - logger := loggingutils.Logger(ctx, "bitflyerCustodian.SubmitTransactions") - - // we need to check the limit total for all txs that they do not exceed a limit JPY - var ( - JPYLimit = decimal.NewFromFloat(100000) - totalJPYTransfer = decimal.Zero - // collapsed transactions per destination - destTxs = map[string][]Transaction{} - // list of quote rates per currency - currencies = map[string]decimal.Decimal{} - ) - - for i := range txs { - // add the currency to the list of all currencies we need rates for - currency, err := txs[i].GetCurrency(ctx) - if err != nil { - msg := "failed to get tx currency" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - // set this as a currency we need to get a rate for - currencies[currency.String()] = decimal.Zero - - // collapse down based on deposit id - destination, err := txs[i].GetDestination(ctx) - if err != nil { - msg := "failed to get tx destination" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - destTxs[destination.String()] = append(destTxs[destination.String()], txs[i]) - } - - // get all the quotes for each of the currencies - for k := range currencies { - quote, err := bc.client.FetchQuote(ctx, fmt.Sprintf("%s_JPY", k), false) - if err != nil { - if isBitflyerErrorUnauthorized(ctx, err) { - // if this was a bitflyer error and the error is due to a 401 response, refresh the token - logger.Debug().Msg("attempting to refresh the bf token") - _, err = bc.client.RefreshToken(ctx, bitflyer.TokenPayloadFromCtx(ctx)) - if err != nil { - msg := "failed to get token from bitflyer.RefreshToken" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - // redo the request after token refresh - quote, err = bc.client.FetchQuote(ctx, fmt.Sprintf("%s_JPY", k), false) - if err != nil { - msg := "failed to fetch bitflyer quote" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - } else { - // non-recoverable error fetching quote - msg := "failed to fetch bitflyer quote" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - } - - // set the rate for the currency - currencies[k] = quote.Rate - } - - // each destination must have all txes collapsed according to bf - var destConsolidatedTxs = []bitflyer.WithdrawToDepositIDPayload{} - - // for each destination we need to collapse all of the transactions into one - for destination, txs := range destTxs { - // to make a combined idempotency key - var ( - transferIDs = []string{} - c string - totalF64 float64 - limitReached bool - ) - - for _, tx := range txs { - idempotencyKey, err := tx.GetIdempotencyKey(ctx) - if err != nil { - // non-recoverable error getting tx amount - msg := "failed to get idempotency key from transaction" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - transferIDs = append(transferIDs, idempotencyKey.String()) - - if !limitReached { - // get the amount of the tx - amount, err := tx.GetAmount(ctx) - if err != nil { - // non-recoverable error getting tx amount - msg := "failed to get amount from transaction" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - t, _ := amount.Float64() - // we cap at the total if we exceed... - totalF64 += t - - // lookup the currency string of this TX so we can figure out if we are over - currency, err := tx.GetCurrency(ctx) - if err != nil { - // non-recoverable error getting tx currency - msg := "failed to get currency from transaction" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - c = currency.String() - - totalJPYTransfer = totalJPYTransfer.Add(amount.Mul(currencies[currency.String()])) - - if totalJPYTransfer.GreaterThan(JPYLimit) { - over := JPYLimit.Sub(totalJPYTransfer).String() - totalF64, _ = JPYLimit.Div(currencies[currency.String()]).Floor().Float64() - - overLimitErr := fmt.Errorf( - "over custodian transfer limit - JPY by %s; %s_JPY rate: %v; BAT: %v", - over, currency.String(), currencies[currency.String()], totalJPYTransfer) - - logger.Warn().Err(overLimitErr).Msg("destination exceeds jpy limit") - limitReached = true - } - } - } - - sourceFrom, ok := ctx.Value(appctx.BitflyerSourceFromCTXKey).(string) - if !ok { - // bad configuration, need source from specified - msg := "failed to get source from context" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, ErrInvalidSource)) - } - - // sort the transferIDs so we can recalculate the transfer_id for this batch of txs - sort.Strings(transferIDs) - - // set up a client payload - destConsolidatedTxs = append(destConsolidatedTxs, bitflyer.WithdrawToDepositIDPayload{ - CurrencyCode: c, - Amount: totalF64, - DepositID: destination, - // transferids uuidv5 with namespace - // deterministic from all tx transfers - TransferID: uuid.NewV5(BitflyerTransferIDNamespace, strings.Join(transferIDs, ",")).String(), - SourceFrom: sourceFrom, - }) - } - - // finally send all these txs to bf - // create a WithdrawToDepositIDBulkPayload - payload := bitflyer.WithdrawToDepositIDBulkPayload{ - Withdrawals: destConsolidatedTxs, - } - // upload - _, err := bc.client.UploadBulkPayout(ctx, payload) - if err != nil { - // non-recoverable error getting tx currency - msg := "failed to perform bulk payout with bitflyer" - return loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - return nil -} - -type bitflyerTransactionStatus struct { - Status string - Message string -} - -func (bts *bitflyerTransactionStatus) String() string { - return bts.Status -} - -// GetTransactionsStatus - implement Custodian interface. within this implementation -// a business requirement of the transaction submission is to collapse all txs per destination -// into one transfer. To do this we take all of the txs in the passed in batch, organize by -// destination, then collapse a sorted list of idempotency keys into an array which is used -// to create a unified batch transfer_id -func (bc *bitflyerCustodian) GetTransactionsStatus(ctx context.Context, txs ...Transaction) (map[string]TransactionStatus, error) { - logger := loggingutils.Logger(ctx, "bitflyerCustodian.GetTransactionsStatus") - var ( - transferIDs = []string{} - destTxs = map[string][]Transaction{} - ) - - // collapse by deposit destination - for i := range txs { - // collapse down based on deposit id - destination, err := txs[i].GetDestination(ctx) - if err != nil { - msg := "failed to get tx destination" - return nil, loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - destTxs[destination.String()] = append(destTxs[destination.String()], txs[i]) - } - - for _, txs := range destTxs { - // to make a combined idempotency key - var ( - idempotencyKeys = []string{} - ) - for _, tx := range txs { - idempotencyKey, err := tx.GetIdempotencyKey(ctx) - if err != nil { - // non-recoverable error getting tx amount - msg := "failed to get idempotency key from transaction" - return nil, loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - // add to transferIDs - idempotencyKeys = append(idempotencyKeys, idempotencyKey.String()) - } - // sort the idempotency keys so we can recalculate the transfer_id for this batch of txs - sort.Strings(idempotencyKeys) - - transferIDs = append(transferIDs, uuid.NewV5(BitflyerTransferIDNamespace, strings.Join(idempotencyKeys, ",")).String()) - } - - resp, err := bc.client.CheckPayoutStatus(ctx, bitflyer.TransferIDsToBulkStatus(transferIDs)) - if err != nil { - if isBitflyerErrorUnauthorized(ctx, err) { - // if this was a bitflyer error and the error is due to a 401 response, refresh the token - logger.Debug().Msg("attempting to refresh the bf token") - _, err = bc.client.RefreshToken(ctx, bitflyer.TokenPayloadFromCtx(ctx)) - if err != nil { - msg := "failed to get token from bitflyer.RefreshToken" - return nil, loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - // redo the request after token refresh - resp, err = bc.client.CheckPayoutStatus(ctx, bitflyer.TransferIDsToBulkStatus(transferIDs)) - if err != nil { - msg := "failed to fetch bitflyer transaction status" - return nil, loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - } else { - // non-recoverable error fetching quote - msg := "failed to fetch bitflyer transaction status" - return nil, loggingutils.LogAndError(logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - } - logger.Debug().Str("resp", fmt.Sprintf("%+v", resp)).Msg("response from check payout status") - - var txStatuses = map[string]TransactionStatus{} - // reconstruct our response - if resp != nil { - if resp.Withdrawals != nil { - for _, v := range resp.Withdrawals { - txStatuses[v.TransferID] = &bitflyerTransactionStatus{ - Status: v.Status, Message: v.Message, - } - } - } - } - - logger.Debug().Str("txStatuses", fmt.Sprintf("%+v", txStatuses)).Msg("result of statuses") - - return txStatuses, nil -} diff --git a/libs/custodian/provider/bitflyer_test.go b/libs/custodian/provider/bitflyer_test.go deleted file mode 100644 index 3b789382e..000000000 --- a/libs/custodian/provider/bitflyer_test.go +++ /dev/null @@ -1,128 +0,0 @@ -//go:build custodianintegration -// +build custodianintegration - -package provider_test - -import ( - "context" - "fmt" - "os" - "strings" - "testing" - - "github.com/brave-intl/bat-go/libs/altcurrency" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/google/uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type BitflyerCustodianTestSuite struct { - suite.Suite - custodian custodian.Custodian - ctx context.Context -} - -func TestBitflyerCustodianTestSuite(t *testing.T) { - suite.Run(t, new(BitflyerCustodianTestSuite)) -} - -func (suite *BitflyerCustodianTestSuite) SetupTest() { - // setup the context - suite.ctx = context.Background() - - // setup debug for client - suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, true) - // setup debug log level - suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "debug") - - // setup a logger and put on context - suite.ctx, _ = logutils.SetupLogger(suite.ctx) - - for _, key := range []appctx.CTXKey{ - appctx.BitFlyerJWTKeyCTXKey, - appctx.BitflyerExtraClientSecretCTXKey, - appctx.BitflyerClientSecretCTXKey, - appctx.BitflyerClientIDCTXKey, - appctx.BitflyerServerURLCTXKey, - appctx.BitflyerProxyURLCTXKey, - appctx.BitflyerTokenCTXKey, - appctx.BitflyerSourceFromCTXKey} { - // setup keys - suite.ctx = context.WithValue(suite.ctx, key, os.Getenv(strings.ToUpper(string(key)))) - } - - var err error - // setup custodian, all configs default to whats in context - suite.custodian, err = custodian.New(suite.ctx, custodian.Config{Provider: "bitflyer"}) - suite.Require().NoError(err, "Must be able to correctly initialize the client") -} - -func (suite *BitflyerCustodianTestSuite) TestSubmitAndGetTransactions() { - var ( - // source is settlement wallet and in bf case there is no source - source = uuid.New() - - // dest is destination wallet - dest1 = uuid.MustParse(os.Getenv("BITFLYER_TEST_DESTINATION_ID")) - dest2 = uuid.MustParse(os.Getenv("BITFLYER_TEST_DESTINATION_ID")) - ) - - // txs - ik1 := uuid.New() - ik2 := uuid.New() - - oneBAT, err := decimal.NewFromString("1") - twoBAT, err := decimal.NewFromString("2") - - tx1, err := custodian.NewTransaction(suite.ctx, &ik1, &dest1, &source, altcurrency.BAT, oneBAT) - suite.Require().NoError(err, "should be able to create transactions") - tx2, err := custodian.NewTransaction(suite.ctx, &ik2, &dest2, &source, altcurrency.BAT, twoBAT) - suite.Require().NoError(err, "should be able to create transactions") - - txs := []custodian.Transaction{tx1, tx2} - - err = suite.custodian.SubmitTransactions(suite.ctx, txs...) - suite.Require().NoError(err, "should be able to submit transactions") - - statusMap, err := suite.custodian.GetTransactionsStatus(suite.ctx, txs...) - suite.Require().NoError(err, "should be able to get transactions status") - - suite.Require().True(len(statusMap) == 1, "status map should have collapsed transaction statuses") -} - -func (suite *BitflyerCustodianTestSuite) TestSubmitAndGetTransactionsOneOff() { - var ( - // source is settlement wallet and in bf case there is no source - source = uuid.New() - - // dest is destination wallet - dest1 = uuid.MustParse("31987fe9-51c2-48d4-8106-76ee5f613da7") - dest2 = uuid.MustParse("31987fe9-51c2-48d4-8106-76ee5f613da8") - ) - - // txs - ik1 := uuid.New() - ik2 := uuid.New() - - fiveBAT, err := decimal.NewFromString("5") - - tx1, err := custodian.NewTransaction(suite.ctx, &ik1, &dest1, &source, altcurrency.BAT, fiveBAT) - suite.Require().NoError(err, "should be able to create transactions") - - tx2, err := custodian.NewTransaction(suite.ctx, &ik2, &dest2, &source, altcurrency.BAT, fiveBAT) - suite.Require().NoError(err, "should be able to create transactions") - - txs := []custodian.Transaction{tx1, tx2} - - err = suite.custodian.SubmitTransactions(suite.ctx, txs...) - suite.Require().NoError(err, "should be able to submit transactions") - - statusMap, err := suite.custodian.GetTransactionsStatus(suite.ctx, txs...) - suite.Require().NoError(err, "should be able to get transactions status") - - fmt.Println(statusMap) - suite.Require().True(len(statusMap) == 1, "status map should have collapsed transaction statuses") -} diff --git a/libs/custodian/provider/custodian.go b/libs/custodian/provider/custodian.go deleted file mode 100644 index 7b22d6ed6..000000000 --- a/libs/custodian/provider/custodian.go +++ /dev/null @@ -1,138 +0,0 @@ -package provider - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/altcurrency" - appctx "github.com/brave-intl/bat-go/libs/context" - loggingutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/google/uuid" - "github.com/shopspring/decimal" -) - -const ( - // Uphold - custodian identifier for uphold - Uphold = "uphold" - // Gemini - custodian identifier for gemini - Gemini = "gemini" - // Bitflyer - custodian identifier for bitflyer - Bitflyer = "bitflyer" -) - -// TransactionStatus - stringer representation of transaction status -type TransactionStatus fmt.Stringer - -// Transaction - interface defining what a transaction is -type Transaction interface { - GetIdempotencyKey(context.Context) (fmt.Stringer, error) - GetAmount(context.Context) (decimal.Decimal, error) - GetCurrency(context.Context) (altcurrency.AltCurrency, error) - GetDestination(context.Context) (fmt.Stringer, error) - GetSource(context.Context) (fmt.Stringer, error) -} - -// implementation of Transaction -type transaction struct { - IdempotencyKey *uuid.UUID `json:"idempotencyKey,omitempty"` - Amount decimal.Decimal `json:"amount,omitempty"` - Currency altcurrency.AltCurrency `json:"currency,omitempty"` - Destination *uuid.UUID `json:"destination,omitempty"` - Source *uuid.UUID `json:"source,omitempty"` -} - -// GetIdempotencyKey - implement transaction -func (t *transaction) GetIdempotencyKey(context.Context) (fmt.Stringer, error) { - - return t.IdempotencyKey, nil -} - -// GetDestination - implement transaction -func (t *transaction) GetDestination(context.Context) (fmt.Stringer, error) { - return t.Destination, nil -} - -// GetSource - implement transaction -func (t *transaction) GetSource(context.Context) (fmt.Stringer, error) { - return t.Source, nil -} - -// GetAmount - implement transaction -func (t *transaction) GetAmount(context.Context) (decimal.Decimal, error) { - return t.Amount, nil -} - -// GetCurrency - implement transaction -func (t *transaction) GetCurrency(context.Context) (altcurrency.AltCurrency, error) { - return t.Currency, nil -} - -// NewTransaction - create a new transaction -func NewTransaction(ctx context.Context, idempotencyKey, destination, source *uuid.UUID, currency altcurrency.AltCurrency, amount decimal.Decimal) (Transaction, error) { - return &transaction{ - IdempotencyKey: idempotencyKey, - Destination: destination, - Source: source, - Currency: currency, - Amount: amount, - }, nil -} - -// Custodian - interface defining what a custodian is -type Custodian interface { - SubmitTransactions(context.Context, ...Transaction) error - GetTransactionsStatus(context.Context, ...Transaction) (map[string]TransactionStatus, error) -} - -// Config - configurations for each custodian -type Config struct { - Provider string - Config map[appctx.CTXKey]interface{} -} - -// String - implement stringer -func (cc *Config) String() string { - // convert to json - b, err := json.Marshal(cc) - if err != nil { - return fmt.Sprintf("failed to marshal Config: %s", err.Error()) - } - return string(b) -} - -var ( - // ErrConfigValidation - error for validation config - ErrConfigValidation = errors.New("failed to validate custodian configuration") -) - -// New - create new custodian -func New(ctx context.Context, conf Config) (Custodian, error) { - logger := loggingutils.Logger(ctx, "custodian.New").With().Str("conf", conf.String()).Logger() - // validate the configuration - logger.Debug().Msg("about to validate custodian config") - _, err := govalidator.ValidateStruct(conf) - if err != nil { - return nil, loggingutils.LogAndError( - &logger, ErrConfigValidation.Error(), fmt.Errorf("%w: %s", ErrConfigValidation, err.Error())) - } - switch conf.Provider { - case Uphold: - logger.Debug().Msg("creating uphold custodian") - return newUpholdCustodian(ctx, conf) - case Gemini: - logger.Debug().Msg("creating gemini custodian") - return newGeminiCustodian(ctx, conf) - case Bitflyer: - logger.Debug().Msg("creating bitflyer custodian") - return newBitflyerCustodian(ctx, conf) - default: - msg := "invalid provider" - return nil, loggingutils.LogAndError( - &logger, msg, fmt.Errorf( - "%w: invalid provider \"%s\" not in (uphold,gemini,bitflyer)", - ErrConfigValidation, conf.Provider)) - } -} diff --git a/libs/custodian/provider/gemini.go b/libs/custodian/provider/gemini.go deleted file mode 100644 index 8aa6e123f..000000000 --- a/libs/custodian/provider/gemini.go +++ /dev/null @@ -1,42 +0,0 @@ -package provider - -import ( - "context" - "fmt" - - "github.com/brave-intl/bat-go/libs/clients/gemini" - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - loggingutils "github.com/brave-intl/bat-go/libs/logging" -) - -// geminiCustodian - implementation of the gemini custodian -type geminiCustodian struct { - client gemini.Client -} - -// newGeminiCustodian - create a new gemini custodian with configuration -func newGeminiCustodian(ctx context.Context, conf Config) (*geminiCustodian, error) { - logger := loggingutils.Logger(ctx, "custodian.newGeminiCustodian").With().Str("conf", conf.String()).Logger() - - // import config to context if not already set, and create bitflyer client - geminiClient, err := gemini.NewWithContext(appctx.MapToContext(ctx, conf.Config)) - if err != nil { - msg := "failed to create client" - return nil, loggingutils.LogAndError(&logger, msg, fmt.Errorf("%s: %w", msg, err)) - } - - return &geminiCustodian{ - client: geminiClient, - }, nil -} - -// SubmitTransactions - implement Custodian interface -func (uc *geminiCustodian) SubmitTransactions(ctx context.Context, txs ...Transaction) error { - return errorutils.ErrNotImplemented -} - -// GetTransactionsStatus - implement Custodian interface -func (uc *geminiCustodian) GetTransactionsStatus(ctx context.Context, txs ...Transaction) (map[string]TransactionStatus, error) { - return nil, errorutils.ErrNotImplemented -} diff --git a/libs/custodian/provider/gemini_test.go b/libs/custodian/provider/gemini_test.go deleted file mode 100644 index b1bcd67aa..000000000 --- a/libs/custodian/provider/gemini_test.go +++ /dev/null @@ -1,94 +0,0 @@ -//go:build custodianintegration -// +build custodianintegration - -package provider_test - -import ( - "context" - "os" - "strings" - "testing" - - "github.com/brave-intl/bat-go/libs/altcurrency" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/google/uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type GeminiCustodianTestSuite struct { - suite.Suite - custodian custodian.Custodian - ctx context.Context -} - -func TestGeminiCustodianTestSuite(t *testing.T) { - suite.Run(t, new(GeminiCustodianTestSuite)) -} - -func (suite *GeminiCustodianTestSuite) SetupTest() { - // setup the context - suite.ctx = context.Background() - - // setup debug for client - suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, true) - // setup debug log level - suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "debug") - - // setup a logger and put on context - suite.ctx, _ = logutils.SetupLogger(suite.ctx) - - for _, key := range []appctx.CTXKey{ - appctx.GeminiBrowserClientIDCTXKey, - appctx.GeminiClientIDCTXKey, - appctx.GeminiClientSecretCTXKey, - appctx.GeminiAPIKeyCTXKey, - appctx.GeminiAPISecretCTXKey, - appctx.GeminiSettlementAddressCTXKey, - appctx.GeminiProxyURLCTXKey, - appctx.GeminiTokenCTXKey, - appctx.GeminiServerURLCTXKey} { - // setup keys - suite.ctx = context.WithValue(suite.ctx, key, os.Getenv(strings.ToUpper(string(key)))) - } - - var err error - // setup custodian, all configs default to whats in context - suite.custodian, err = custodian.New(suite.ctx, custodian.Config{Provider: "gemini"}) - suite.Require().NoError(err, "Must be able to correctly initialize the client") -} - -func (suite *GeminiCustodianTestSuite) TestSubmitAndGetTransactions() { - var ( - // source is settlement wallet and in bf case there is no source - source = uuid.New() - - // dest is destination wallet - dest1 = uuid.MustParse(os.Getenv("GEMINI_TEST_DESTINATION_ID")) - dest2 = uuid.MustParse(os.Getenv("GEMINI_TEST_DESTINATION_ID")) - ) - - // txs - ik1 := uuid.New() - ik2 := uuid.New() - - oneBAT, err := decimal.NewFromString("1") - twoBAT, err := decimal.NewFromString("2") - - tx1, err := custodian.NewTransaction(suite.ctx, &ik1, &dest1, &source, altcurrency.BAT, oneBAT) - suite.Require().NoError(err, "should be able to create transactions") - tx2, err := custodian.NewTransaction(suite.ctx, &ik2, &dest2, &source, altcurrency.BAT, twoBAT) - suite.Require().NoError(err, "should be able to create transactions") - - txs := []custodian.Transaction{tx1, tx2} - - err = suite.custodian.SubmitTransactions(suite.ctx, txs...) - suite.Require().NoError(err, "should be able to submit transactions") - - statusMap, err := suite.custodian.GetTransactionsStatus(suite.ctx, txs...) - suite.Require().NoError(err, "should be able to get transactions status") - - suite.Require().True(len(statusMap) == 1, "status map should have collapsed transaction statuses") -} diff --git a/libs/custodian/provider/uphold.go b/libs/custodian/provider/uphold.go deleted file mode 100644 index 41ca9f91c..000000000 --- a/libs/custodian/provider/uphold.go +++ /dev/null @@ -1,31 +0,0 @@ -package provider - -import ( - "context" - - "github.com/brave-intl/bat-go/libs/logging" -) - -// upholdCustodian - implementation of the uphold custodian -type upholdCustodian struct{} - -// newUpholdCustodian - create a new uphold custodian with configuration -func newUpholdCustodian(ctx context.Context, conf Config) (*upholdCustodian, error) { - logger := logging.Logger(ctx, "uphold.newUpholdCustodian") - logger.Error().Msg("not yet implemented") - return &upholdCustodian{}, nil -} - -// SubmitTransactions - implement Custodian interface -func (uc *upholdCustodian) SubmitTransactions(ctx context.Context, txs ...Transaction) error { - logger := logging.Logger(ctx, "uphold.SubmitTransactions") - logger.Error().Msg("not yet implemented") - return nil -} - -// GetTransactionsStatus - implement Custodian interface -func (uc *upholdCustodian) GetTransactionsStatus(ctx context.Context, tx ...Transaction) (map[string]TransactionStatus, error) { - logger := logging.Logger(ctx, "uphold.GetTransactionsStatus") - logger.Error().Msg("not yet implemented") - return nil, nil -} diff --git a/libs/custodian/regions.go b/libs/custodian/regions.go deleted file mode 100644 index 8fb7dc0fb..000000000 --- a/libs/custodian/regions.go +++ /dev/null @@ -1,166 +0,0 @@ -package custodian - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/asaskevich/govalidator" - "github.com/aws/aws-sdk-go-v2/service/s3" - appaws "github.com/brave-intl/bat-go/libs/aws" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" -) - -var ( - custodianRegionsObj = "custodian-regions.json" - payoutStatusObj = "payout-status.json" -) - -// ExtractCustodianRegions - extract the custodian regions from the client -func ExtractCustodianRegions(ctx context.Context, client appaws.S3GetObjectAPI, bucket string) (*Regions, error) { - logger := logging.Logger(ctx, "custodian.ExtractCustodianRegions") - // get the object with the client - out, err := client.GetObject( - ctx, &s3.GetObjectInput{ - Bucket: &bucket, - Key: &custodianRegionsObj, - }) - if err != nil { - return nil, fmt.Errorf("failed to get custodian regions: %w", err) - } - defer func() { - if err := out.Body.Close(); err != nil { - logger.Error().Err(err).Msg("failed to close s3 result body") - } - }() - var custodianRegions = new(Regions) - - // parse body json - if err := inputs.DecodeAndValidateReader(ctx, custodianRegions, out.Body); err != nil { - return nil, custodianRegions.HandleErrors(err) - } - - return custodianRegions, nil -} - -// ExtractPayoutStatus - extract the custodian payout status from the client -func ExtractPayoutStatus(ctx context.Context, client appaws.S3GetObjectAPI, bucket string) (*PayoutStatus, error) { - logger := logging.Logger(ctx, "custodian.extractPayoutStatus") - // get the object with the client - out, err := client.GetObject( - ctx, &s3.GetObjectInput{ - Bucket: &bucket, - Key: &payoutStatusObj, - }) - if err != nil { - return nil, fmt.Errorf("failed to get payout status: %w", err) - } - defer func() { - if err := out.Body.Close(); err != nil { - logger.Error().Err(err).Msg("failed to close s3 result body") - } - }() - var payoutStatus = new(PayoutStatus) - - // parse body json - if err := inputs.DecodeAndValidateReader(ctx, payoutStatus, out.Body); err != nil { - return nil, payoutStatus.HandleErrors(err) - } - - return payoutStatus, nil -} - -// HandleErrors - handle any errors in input -func (ps *PayoutStatus) HandleErrors(err error) *handlers.AppError { - return handlers.ValidationError("invalid payout status", err) -} - -// Decode - implement decodable -func (ps *PayoutStatus) Decode(ctx context.Context, input []byte) error { - return json.Unmarshal(input, ps) -} - -// Validate - implement validatable -func (ps *PayoutStatus) Validate(ctx context.Context) error { - isValid, err := govalidator.ValidateStruct(ps) - if err != nil { - return err - } - if !isValid { - return errors.New("invalid input") - } - return nil -} - -// PayoutStatus - current state of the payout status -type PayoutStatus struct { - Unverified string `json:"unverified" valid:"in(off|processing|complete)"` - Uphold string `json:"uphold" valid:"in(off|processing|complete)"` - Gemini string `json:"gemini" valid:"in(off|processing|complete)"` - Bitflyer string `json:"bitflyer" valid:"in(off|processing|complete)"` - Zebpay string `json:"zebpay" valid:"in(off|processing|complete)"` - Date string `json:"payoutDate" valid:"-"` -} - -// GeoAllowBlockMap - this is the allow / block list of geos for a custodian -type GeoAllowBlockMap struct { - Allow []string `json:"allow" valid:"-"` - Block []string `json:"block" valid:"-"` -} - -// check if passed in countries exist in an allow or block list -func contains(countries, allowblock []string) bool { - for _, ab := range allowblock { - for _, country := range countries { - if strings.EqualFold(ab, country) { - fmt.Println("contains ", ab, country) - return true - } - } - } - return false -} - -// Verdict - test is countries exist in allow list, or do not exist in block list -func (gabm GeoAllowBlockMap) Verdict(countries ...string) bool { - if len(gabm.Allow) > 0 { - // allow list exists, use it to check if any countries exist in allow - return contains(countries, gabm.Allow) - } - // check if any block list countries exist in our list of countries - return !contains(gabm.Block, countries) -} - -// Regions - Supported Regions -type Regions struct { - Uphold GeoAllowBlockMap `json:"uphold" valid:"-"` - Gemini GeoAllowBlockMap `json:"gemini" valid:"-"` - Bitflyer GeoAllowBlockMap `json:"bitflyer" valid:"-"` - Zebpay GeoAllowBlockMap `json:"zebpay" valid:"-"` -} - -// HandleErrors - handle any errors in input -func (cr *Regions) HandleErrors(err error) *handlers.AppError { - return handlers.ValidationError("invalid custodian regions", err) -} - -// Decode - implement decodable -func (cr *Regions) Decode(ctx context.Context, input []byte) error { - return json.Unmarshal(input, cr) -} - -// Validate - implement validatable -func (cr *Regions) Validate(ctx context.Context) error { - isValid, err := govalidator.ValidateStruct(cr) - if err != nil { - return err - } - if !isValid { - return errors.New("invalid input") - } - return nil -} diff --git a/libs/custodian/regions_test.go b/libs/custodian/regions_test.go deleted file mode 100644 index 0b2889cce..000000000 --- a/libs/custodian/regions_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package custodian - -import "testing" - -func TestVerdictAllowList(t *testing.T) { - gabm := GeoAllowBlockMap{ - Allow: []string{"US", "FR"}, - } - if gabm.Verdict("CA") { - t.Error("should have failed, CA not in allow list") - } - - if !gabm.Verdict("US") { - t.Error("should have passed, US in allow list") - } -} - -func TestVerdictBlockList(t *testing.T) { - gabm := GeoAllowBlockMap{ - Block: []string{"US", "FR"}, - } - if !gabm.Verdict("CA") { - t.Error("should have been true, CA not in block list") - } - - if gabm.Verdict("US") { - t.Error("should have been false, US in block list") - } -} diff --git a/libs/custodian/transaction.go b/libs/custodian/transaction.go deleted file mode 100644 index ab0a8c091..000000000 --- a/libs/custodian/transaction.go +++ /dev/null @@ -1,84 +0,0 @@ -package custodian - -import ( - "crypto/sha256" - "fmt" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/shengdoushi/base58" - "github.com/shopspring/decimal" -) - -// Transaction describes a payout transaction from the settlement wallet to a publisher -type Transaction struct { - AltCurrency *altcurrency.AltCurrency `json:"altcurrency"` - Authority string `json:"authority"` - Amount decimal.Decimal `json:"amount"` - ExchangeFee decimal.Decimal `json:"commission"` - FailureReason string `json:"failureReason,omitempty"` - Currency string `json:"currency"` - Destination string `json:"address"` - Publisher string `json:"owner"` - BATPlatformFee decimal.Decimal `json:"fees"` - Probi decimal.Decimal `json:"probi"` - ProviderID string `json:"hash"` - WalletProvider string `json:"walletProvider"` - WalletProviderID string `json:"walletProviderId"` - Channel string `json:"publisher"` - SignedTx string `json:"signedTx"` - Status string `json:"status"` - SettlementID string `json:"transactionId" valid:"uuidv4"` - TransferFee decimal.Decimal `json:"fee"` - Type string `json:"type"` - ValidUntil time.Time `json:"validUntil,omitempty"` - DocumentID string `json:"documentId,omitempty"` - Note string `json:"note"` -} - -// TransferID generate the transfer id -func (tx Transaction) TransferID() string { - inputs := []string{ - tx.SettlementID, - tx.Destination, - tx.Channel, - } - key := strings.Join(inputs, "_") - bytes := sha256.Sum256([]byte(key)) - refID := base58.Encode(bytes[:], base58.IPFSAlphabet) - return refID -} - -// BitflyerTransferID generate the bitflier transfer id -func (tx Transaction) BitflyerTransferID() string { - inputs := []string{ - tx.SettlementID, - tx.WalletProviderID, - tx.Destination, - } - key := strings.Join(inputs, "_") - bytes := sha256.Sum256([]byte(key)) - refID := base58.Encode(bytes[:], base58.IPFSAlphabet) - return refID -} - -// Log logs a message -func (tx Transaction) Log() { - fmt.Println(tx.Destination, tx.Publisher, tx.TransferID(), tx.Channel) -} - -// IsProcessing returns true if the transaction status is processing -func (tx Transaction) IsProcessing() bool { - return tx.Status == "processing" -} - -// IsFailed returns true if the transaction status is failed -func (tx Transaction) IsFailed() bool { - return tx.Status == "failed" -} - -// IsComplete returns true if the transaction status is completed -func (tx Transaction) IsComplete() bool { - return tx.Status == "completed" -} diff --git a/libs/datastore/models.go b/libs/datastore/models.go deleted file mode 100644 index 1176bb901..000000000 --- a/libs/datastore/models.go +++ /dev/null @@ -1,60 +0,0 @@ -package datastore - -import ( - "database/sql" - "database/sql/driver" - "encoding/json" - "errors" - "strings" -) - -// Metadata - type which represents key/value pair metadata -type Metadata map[string]interface{} - -// Value - implement driver.Valuer interface for conversion to and from sql -func (m Metadata) Value() (driver.Value, error) { - return json.Marshal(m) -} - -// Scan - implement driver.Scanner interface for conversion to and from sql -func (m *Metadata) Scan(value interface{}) error { - if value == nil { - return nil - } - b, ok := value.([]byte) - if !ok { - return errors.New("failed to scan Metadata, not byte slice") - } - - // BUG: numbers stored in jsonb become float64 when retrieved. - // - // If there was an integer stored as jsonb on a table, - // when fetched, it will appear as float64 in the map. - // - // This is due to how Go treats JSON numbers when the destination is interface{}. - // See docs for [Unmarshal]](https://pkg.go.dev/encoding/json#Unmarshal). - return json.Unmarshal(b, &m) -} - -// NullString is a type that lets ya get a null field from the database -type NullString struct { - sql.NullString -} - -// MarshalJSON for NullString -func (ns *NullString) MarshalJSON() ([]byte, error) { - if !ns.Valid { - return []byte("null"), nil - } - return json.Marshal(ns.String) -} - -// UnmarshalJSON unmarshalls NullString -func (ns *NullString) UnmarshalJSON(data []byte) error { - ns.String = strings.Trim(string(data), `"`) - ns.Valid = true - if len(data) == 0 { - ns.Valid = false - } - return nil -} diff --git a/libs/datastore/postgres.go b/libs/datastore/postgres.go deleted file mode 100644 index 1fde6af11..000000000 --- a/libs/datastore/postgres.go +++ /dev/null @@ -1,242 +0,0 @@ -package datastore - -import ( - "context" - "database/sql" - "errors" - "fmt" - "os" - "strconv" - "strings" - "time" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/metrics" - sentry "github.com/getsentry/sentry-go" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/golang-migrate/migrate/v4/database/postgres" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - - // needed for magic migration - _ "github.com/golang-migrate/migrate/v4/source/file" -) - -var ( - // dbInstanceClassToMaxConn - https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Managing.html - dbInstanceClassToMaxConn = map[string]int{ - "db.r4.large": 1600, - "db.r4.xlarge": 3200, - "db.r4.2xlarge": 5000, - "db.r4.4xlarge": 5000, - "db.r4.8xlarge": 5000, - "db.r4.16xlarge": 5000, - "db.r5.large": 1600, - "db.r5.xlarge": 3300, - "db.r5.2xlarge": 5000, - "db.r5.4xlarge": 5000, - "db.r5.12xlarge": 5000, - "db.r5.24xlarge": 5000, - } - dbs = map[string]*sqlx.DB{} - // CurrentMigrationVersion holds the default migration version - CurrentMigrationVersion = uint(63) - // MigrationTracks holds the migration version for a given track (eyeshade, promotion, wallet) - MigrationTracks = map[string]uint{ - "eyeshade": 20, - } -) - -// Datastore holds generic methods -type Datastore interface { - RawDB() *sqlx.DB - NewMigrate() (*migrate.Migrate, error) - Migrate(...uint) error - RollbackTxAndHandle(tx *sqlx.Tx) error - RollbackTx(tx *sqlx.Tx) - BeginTx() (*sqlx.Tx, error) -} - -// Postgres is a Datastore wrapper around a postgres database -type Postgres struct { - *sqlx.DB -} - -// RawDB - get the raw db -func (pg *Postgres) RawDB() *sqlx.DB { - return pg.DB -} - -// NewMigrate creates a Migrate instance given a Postgres instance with an active database connection -func (pg *Postgres) NewMigrate() (*migrate.Migrate, error) { - driver, err := postgres.WithInstance(pg.RawDB().DB, &postgres.Config{}) - if err != nil { - return nil, err - } - - dbMigrationsURL := os.Getenv("DATABASE_MIGRATIONS_URL") - m, err := migrate.NewWithDatabaseInstance( - dbMigrationsURL, - "postgres", - driver, - ) - if err != nil { - return nil, err - } - - return m, err -} - -// Migrate the Postgres instance -func (pg *Postgres) Migrate(currentMigrationVersions ...uint) error { - ctx := context.WithValue(context.Background(), appctx.EnvironmentCTXKey, os.Getenv("ENV")) - _, logger := logging.SetupLogger(ctx) - - logger.Info().Msg("attempting database migration") - - m, err := pg.NewMigrate() - if err != nil { - logger.Error().Err(err).Msg("failed to create a new migration") - return err - } - - activeMigrationVersion, dirty, err := m.Version() - - currentMigrationVersion := CurrentMigrationVersion - if len(currentMigrationVersions) > 0 { - currentMigrationVersion = currentMigrationVersions[0] - } - - subLogger := logger.With(). - Bool("dirty", dirty). - Int("db_version", int(activeMigrationVersion)). - Uint("code_version", currentMigrationVersion). - Logger() - - subLogger.Info().Msg("database status") - - if !errors.Is(err, migrate.ErrNilVersion) && err != nil { - subLogger.Error().Err(err).Msg("failed to get migration version") - sentry.CaptureMessage(err.Error()) - return fmt.Errorf("failed to get migration version: %w", err) - } - - // Don't attempt the migration if our currentMigrationVersion is less than the active db version or if the migration is in dirty state - if currentMigrationVersion < activeMigrationVersion || dirty { - subLogger.Error().Msg("migration not attempted") - sentry.CaptureMessage( - fmt.Sprintf("migration not attempted, dirty: %t; code version: %d; db version: %d", - dirty, currentMigrationVersion, activeMigrationVersion)) - return nil - } - - err = m.Migrate(currentMigrationVersion) - if err != migrate.ErrNoChange && err != nil { - subLogger.Error().Err(err).Msg("migration failed") - return err - } - - return nil -} - -// NewPostgres creates a new Postgres Datastore -func NewPostgres( - databaseURL string, - performMigration bool, - migrationTrack string, - dbStatsPrefix ...string, -) (*Postgres, error) { - if len(databaseURL) == 0 { - databaseURL = os.Getenv("DATABASE_URL") - } - dbStatsPref := strings.Join(dbStatsPrefix, "_") - - key := dbStatsPref + ":" + databaseURL - - if dbs[key] != nil { - return &Postgres{dbs[key]}, nil - } - - db, err := sqlx.Open("postgres", databaseURL) - if err != nil { - return nil, err - } - - dbs[key] = db - - // setup instrumentation using sqlstats - if len(dbStatsPrefix) > 0 { - // Create a new collector, the name will be used as a label on the metrics - collector := metrics.NewStatsCollector(dbStatsPref, db) - // Register it with Prometheus - err := prometheus.Register(collector) - - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - // take old collector, and add the new db - if sc, ok := ae.ExistingCollector.(*metrics.StatsCollector); ok { - sc.AddStatsGetter(dbStatsPref, db) - } - } - } - - // if we have a connection longer than 5 minutes, kill it - db.SetConnMaxLifetime(5 * time.Minute) - - // set max open connections to default to 80 (will get overwritten later by calculation - // depending of if we have environment variables set - maxOpenConns := 80 - - // using desired/max tasks to calculate the right number of max open connections - desiredTasks, dterr := strconv.Atoi(os.Getenv("DESIRED_TASKS")) - maxTasks, mterr := strconv.Atoi(os.Getenv("MAXIMUM_TASKS")) - - if dterr == nil && mterr == nil && desiredTasks > 0 && maxTasks > 0 { - grantDbInstanceClass := os.Getenv("GRANT_DB_INSTANCE_CLASS") - // 3300 / maxTasks desiredTasks - if maxConns, ok := dbInstanceClassToMaxConn[grantDbInstanceClass]; ok { - // if we are able to get desired tasks, max tasks and instance class from environment - // also taking into account that payments/grants/promotions are all using the same database instance to - // calculate the max open connections: - maxOpenConns = maxConns / (maxTasks / desiredTasks) / 3 - } - } - - db.SetMaxOpenConns(maxOpenConns) - // 50% of max open - db.SetMaxIdleConns(maxOpenConns / 2) - - pg := &Postgres{db} - - if performMigration { - migrationVersion := MigrationTracks[migrationTrack] - if migrationVersion == 0 { - migrationVersion = CurrentMigrationVersion - } - err = pg.Migrate(migrationVersion) - if err != nil { - return nil, err - } - } - - return pg, nil -} - -// RollbackTxAndHandle rolls back a transaction -func (pg *Postgres) RollbackTxAndHandle(tx *sqlx.Tx) error { - err := tx.Rollback() - if err != nil && err != sql.ErrTxDone { - sentry.CaptureMessage(err.Error()) - } - return err -} - -// RollbackTx rolls back a transaction (useful with defer) -func (pg *Postgres) RollbackTx(tx *sqlx.Tx) { - _ = pg.RollbackTxAndHandle(tx) -} - -// BeginTx starts a transaction -func (pg *Postgres) BeginTx() (*sqlx.Tx, error) { - return pg.RawDB().Beginx() -} diff --git a/libs/datastore/tx.go b/libs/datastore/tx.go deleted file mode 100644 index a4d06d110..000000000 --- a/libs/datastore/tx.go +++ /dev/null @@ -1,62 +0,0 @@ -package datastore - -import ( - "context" - "fmt" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/jmoiron/sqlx" -) - -// TxAble - something that is capable of beginning and rolling back a sqlx.Tx -type TxAble interface { - RollbackTx(*sqlx.Tx) - BeginTx() (*sqlx.Tx, error) -} - -// GetTx will get or create a tx on the context, if created hands back rollback and commit functions -func GetTx(ctx context.Context, ta TxAble) (context.Context, *sqlx.Tx, func(), func() error, error) { - logger := logging.Logger(ctx, "datastore.GetTx") - // get tx - tx, noContextTx := ctx.Value(appctx.DatabaseTransactionCTXKey).(*sqlx.Tx) - if !noContextTx { - tx, err := CreateTx(ctx, ta) - if err != nil || tx == nil { - logger.Error().Err(err).Msg("error creating tx") - return ctx, nil, func() {}, func() error { return nil }, fmt.Errorf("failed to create tx: %w", err) - } - ctx = context.WithValue(ctx, appctx.DatabaseTransactionCTXKey, tx) - return ctx, tx, rollbackFn(ctx, ta, tx), commitFn(ctx, tx), nil - } - return ctx, tx, func() {}, func() error { return nil }, nil -} - -func rollbackFn(ctx context.Context, ta TxAble, tx *sqlx.Tx) func() { - return func() { - ta.RollbackTx(tx) - } -} - -func commitFn(ctx context.Context, tx *sqlx.Tx) func() error { - logger := logging.Logger(ctx, "datastore.commitFn") - return func() error { - if err := tx.Commit(); err != nil { - logger.Error().Err(err).Msg("failed to commit transaction") - return err - } - return nil - } -} - -// CreateTx - helper to create a tx -func CreateTx(ctx context.Context, ta TxAble) (tx *sqlx.Tx, err error) { - logger := logging.Logger(ctx, "datastore.CreateTx") - tx, err = ta.BeginTx() - if err != nil { - logger.Error().Err(err). - Msg("error creating transaction") - return tx, fmt.Errorf("failed to create transaction: %w", err) - } - return tx, nil -} diff --git a/libs/digest/digest.go b/libs/digest/digest.go deleted file mode 100644 index 8ac700bd1..000000000 --- a/libs/digest/digest.go +++ /dev/null @@ -1,79 +0,0 @@ -// Package digest implements an instance which serializes to / from the Digest header per rfc3230 -// https://tools.ietf.org/html/rfc3230 -package digest - -import ( - "crypto" - _ "crypto/sha256" // Needed since crypto.SHA256 does not actually pull in implementation - _ "crypto/sha512" // Needed since crypto.SHA512 does not actually pull in implementation - "encoding/base64" - "errors" - "fmt" - "strings" -) - -// Instance consists of the hash type and an internal digest value -type Instance struct { - crypto.Hash - Digest string -} - -var hashName = map[crypto.Hash]string{ - crypto.SHA256: "SHA-256", - crypto.SHA512: "SHA-512", -} - -var hashID = map[string]crypto.Hash{ - "SHA-256": crypto.SHA256, - "SHA-512": crypto.SHA512, -} - -// Return a string representation digest in HTTP Digest header format -func (d *Instance) String() string { - return fmt.Sprintf("%s=%s", hashName[d.Hash], d.Digest) -} - -// MarshalText returns the marshalled digest in HTTP Digest header format -func (d *Instance) MarshalText() (text []byte, err error) { - return []byte(d.String()), nil -} - -// UnmarshalText parses the digest from HTTP Digest header format -func (d *Instance) UnmarshalText(text []byte) (err error) { - s := strings.SplitN(string(text), "=", 2) - if len(s) != 2 { - return errors.New("a valid digest specifier must consist of two parts separated by =") - } - var exists bool - d.Hash, exists = hashID[s[0]] - if !exists { - return fmt.Errorf("the digest algorithm %s is not supported", s[0]) - } - d.Digest = s[1] - return nil -} - -// Calculate but do not update the internal digest value for b. -// Subsequent calls to MarshalText, String, etc will not use the returned value. -func (d *Instance) Calculate(b []byte) string { - hash := d.New() - _, err := hash.Write(b) - if err != nil { - panic(err) - } - var out []byte - return base64.StdEncoding.EncodeToString(hash.Sum(out)) -} - -// Update (after calculating) the internal digest value for b. -// Subsequent calls to MarshalText, String, etc will use the updated value. -func (d *Instance) Update(b []byte) { - d.Digest = d.Calculate(b) -} - -// Verify by calculating the digest value for b and checking against the internal digest value. -// Returns true if the digest values match. -func (d *Instance) Verify(b []byte) bool { - expected := d.Calculate(b) - return d.Digest == expected -} diff --git a/libs/digest/digest_test.go b/libs/digest/digest_test.go deleted file mode 100644 index 0cec0923e..000000000 --- a/libs/digest/digest_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package digest - -import ( - "crypto" - "testing" -) - -func TestMarshalText(t *testing.T) { - var d Instance - d.Hash = crypto.SHA256 - d.Digest = "=FOOBAR==" - b, err := d.MarshalText() - if err != nil { - t.Error("Unexpected error during marshal") - } - if string(b) != "SHA-256==FOOBAR==" { - t.Error("Incorrect marshal") - } -} - -func TestUnmarshalText(t *testing.T) { - var expected, d Instance - expected.Hash = crypto.SHA256 - expected.Digest = "=FOOBAR==" - - str := "SHA-256==FOOBAR==" - err := d.UnmarshalText([]byte(str)) - if err != nil { - t.Error("Unexpected error during unmarshal") - } - if d != expected { - t.Error("Incorrect unmarshal") - } -} - -func TestCalculate(t *testing.T) { - expected := "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=" - var d Instance - d.Hash = crypto.SHA256 - out := d.Calculate([]byte("hello world")) - if out != expected { - t.Error("Incorrect calc") - } -} - -func TestUpdate(t *testing.T) { - expected := "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=" - var d Instance - d.Hash = crypto.SHA256 - d.Update([]byte("hello world")) - if d.Digest != expected { - t.Error("Incorrect update") - } -} - -func TestVerify(t *testing.T) { - var d Instance - err := d.UnmarshalText([]byte("SHA-256=uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=")) - if err != nil { - t.Error("Unexpected error during unmarshal") - } - - if !d.Verify([]byte("hello world")) { - t.Error("Incorrect calc") - } - - if d.Verify([]byte("foo bar")) { - t.Error("Incorrect calc") - } -} diff --git a/libs/errors/errors.go b/libs/errors/errors.go deleted file mode 100644 index f23653187..000000000 --- a/libs/errors/errors.go +++ /dev/null @@ -1,214 +0,0 @@ -package errors - -import ( - "encoding/json" - "errors" - "fmt" -) - -var ( - // ErrInvalidCountry - invalid country error for validation - ErrInvalidCountry = errors.New("invalid country") - // ErrNoIdentityCountry - no specified identity country - ErrNoIdentityCountry = errors.New("no identity country") - // ErrConflictBAPReportEvent is an error created when trying to update a bat loss event with a different amount - ErrConflictBAPReportEvent = errors.New("unable to record BAP report") - // ErrConflictBATLossEvent is an error created when trying to update a bat loss event with a different amount - ErrConflictBATLossEvent = errors.New("unable to update bat loss events") - // ErrCertificateExpired - a certificate is expired - ErrCertificateExpired = errors.New("certificate expired") - // ErrMarshalTransferRequest - failed to marshal the transfer request - ErrMarshalTransferRequest = errors.New("failed to marshal the transfer request") - // ErrCreateTransferRequest - failed to create the transfer request - ErrCreateTransferRequest = errors.New("failed to create the transfer request") - // ErrSignTransferRequest - failed to sign the transfer request - ErrSignTransferRequest = errors.New("failed to sign the transfer request") - // ErrFailedClientRequest - failed to perform client request - ErrFailedClientRequest = errors.New("failed to perform api request") - // ErrFailedBodyRead - failed to read body - ErrFailedBodyRead = errors.New("failed to read the transfer response") - // ErrFailedBodyUnmarshal - failed to decode body - ErrFailedBodyUnmarshal = errors.New("failed to unmarshal the transfer response") - // ErrMissingWallet - missing wallet - ErrMissingWallet = errors.New("missing wallet") - // ErrNoDepositProviderDestination - no linked wallet - ErrNoDepositProviderDestination = errors.New("no deposit provider destination for wallet for transfer") - // ErrNotImplemented - this function is not yet implemented - ErrNotImplemented = errors.New("this function is not yet implemented") - // ErrNotFound - resource not found - ErrNotFound = errors.New("not found") - // ErrInternalServerError internal server error - ErrInternalServerError = errors.New("server encountered an internal error and was unable to complete the request") - // ErrBadRequest bad request error - ErrBadRequest = errors.New("error bad request") -) - -// ErrorBundle creates a new response error -type ErrorBundle struct { - cause error - message string - data interface{} -} - -// New creates a new response error -func New(cause error, message string, data interface{}) error { - return &ErrorBundle{ - cause, - message, - data, - } -} - -// Data from error origin -func (e ErrorBundle) Data() interface{} { - return e.data -} - -// Cause returns the associated cause -func (e ErrorBundle) Cause() error { - return e.cause -} - -// Unwrap returns the associated cause -func (e ErrorBundle) Unwrap() error { - return e.cause -} - -// Error turns into an error -func (e ErrorBundle) Error() string { - return e.message -} - -// DataToString returns string representation of data -func (e ErrorBundle) DataToString() string { - if e.data == nil { - return "no error bundle data" - } - b, err := json.Marshal(e.data) - if err != nil { - return fmt.Sprintf("error retrieving error bundle data %s", err.Error()) - } - return string(b) -} - -// Wrap wraps an error -func Wrap(cause error, message string) error { - return &ErrorBundle{ - cause: cause, - message: message, - data: nil, - } -} - -// MultiError - allows for multiple errors, not necessarily chained -type MultiError struct { - Errs []error -} - -// Append - append new errors to this multierror -func (me *MultiError) Append(err ...error) { - if me.Errs == nil { - me.Errs = []error{} - } - me.Errs = append(me.Errs, err...) -} - -// Count - get the number of errors contained herein -func (me *MultiError) Count() int { - return len(me.Errs) -} - -type wErrs struct { - err error - cause error -} - -func (we *wErrs) Cause(err error) { - we.cause = err -} - -func (we *wErrs) Error() string { - var result string - if we.err != nil { - result = we.err.Error() - } - if we.cause != nil { - result += ": " + we.cause.Error() - } - return result -} - -// Is - implement interface{ Is(error) bool } for equality check -func (we *wErrs) Is(err error) bool { - return err == we.err -} - -// As - implement interface{ As(target interface{}) bool } for equality check -func (we *wErrs) As(target interface{}) bool { - return errors.As(we.err, target) -} - -// Unwrap - implement unwrap interface to get the cause -func (we *wErrs) Unwrap() error { - return we.cause -} - -// Unwrap - implement Unwrap for unwrapping sub errors -func (me *MultiError) Unwrap() error { - var errs []error - // iterate over all the errors and wrapped errors - // make a list so we can put them in wErr nodes - for _, v := range me.Errs { - vv := v - for { - errs = append(errs, vv) - // unwrap until cant - err := errors.Unwrap(vv) - if err == nil { - break - } - vv = err - } - } - - var wrappedErr = new(wErrs) - for _, v := range errs { - if v != nil { - wrappedErr = &wErrs{err: v, cause: wrappedErr} - } - } - wrappedErr = &wErrs{err: errors.New("wrapped errors"), cause: wrappedErr} - - return wrappedErr - -} - -// Error - implement Error interface -func (me *MultiError) Error() string { - var errText string - for _, err := range me.Errs { - if errText == "" { - errText = fmt.Sprintf("%s", err) - } else { - errText += fmt.Sprintf("; %s", err) - } - } - return errText -} - -// DrainCodified - Job runner drain codified errors have DrainCode() -type DrainCodified interface { - // DrainCode - get the drain code from the interface implementation - DrainCode() (string, bool) -} - -// Codified - implementation of DrainCodified -type Codified struct { - ErrCode string - Retry bool -} - -// DrainCode - implementation of DrainCodified.DrainCode -func (c Codified) DrainCode() (string, bool) { - return c.ErrCode, c.Retry -} diff --git a/libs/errors/errors_test.go b/libs/errors/errors_test.go deleted file mode 100644 index 8a127e693..000000000 --- a/libs/errors/errors_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package errors_test - -import ( - "encoding/json" - "errors" - "fmt" - "testing" - - testutils "github.com/brave-intl/bat-go/libs/test" - "github.com/stretchr/testify/assert" - - errutil "github.com/brave-intl/bat-go/libs/errors" -) - -type customErr struct{} - -func (ce *customErr) Error() string { - return "custom error" -} - -func TestMultiErrorUnwrap(t *testing.T) { - var ( - err1b = errors.New("error 1b") - err1a = fmt.Errorf("error 1a: %w", err1b) - err1 = fmt.Errorf("error 1: %w", err1a) - err2 = errors.New("error 2") - err3 = &customErr{} - ) - merr := &errutil.MultiError{} - merr.Append(err1, err2, err3) - - var myCustomErr *customErr - if !errors.As(merr, &myCustomErr) { - t.Error("failed to unwrap multierror correctly: not 'as' err3") - } - - if !errors.Is(merr, err1a) { - t.Error("failed to unwrap multierror correctly: not 'is' err1a") - } - - if !errors.Is(merr, err1b) { - t.Error("failed to unwrap multierror correctly: not 'is' err1b") - } - - if !errors.Is(merr, err1) { - t.Error("failed to unwrap multierror correctly: not 'is' err1") - } - - if !errors.Is(merr, err2) { - t.Error("failed to unwrap multierror correctly: not 'is' err2") - } -} - -func TestErrorBundle_DataToString_DataNil(t *testing.T) { - err := errutil.Wrap(errors.New(testutils.RandomString()), testutils.RandomString()) - var actual *errutil.ErrorBundle - errors.As(err, &actual) - assert.Equal(t, "no error bundle data", actual.DataToString()) -} - -func TestErrorBundle_DataToString_MarshallError(t *testing.T) { - unsupportedData := func() {} - sut := errutil.New(errors.New(testutils.RandomString()), testutils.RandomString(), unsupportedData) - - expected := "error retrieving error bundle data" - - var actual *errutil.ErrorBundle - errors.As(sut, &actual) - - assert.Contains(t, actual.DataToString(), expected) -} - -func TestErrorBundle_DataToString(t *testing.T) { - errorData := testutils.RandomString() - sut := errutil.New(errors.New(testutils.RandomString()), testutils.RandomString(), errorData) - - expected, err := json.Marshal(errorData) - assert.NoError(t, err) - - var actual *errutil.ErrorBundle - errors.As(sut, &actual) - - assert.Equal(t, string(expected), actual.DataToString()) -} diff --git a/libs/errors/helpers.go b/libs/errors/helpers.go deleted file mode 100644 index c75466a22..000000000 --- a/libs/errors/helpers.go +++ /dev/null @@ -1,64 +0,0 @@ -package errors - -// IsErrNotFound is a helper method for determining if an error indicates a missing resource -func IsErrNotFound(err error) bool { - type notFound interface { - NotFoundError() bool - } - te, ok := err.(notFound) - return ok && te.NotFoundError() -} - -// IsErrInvalidDestination is a helper method for determining if an error indicates an invalid destination -func IsErrInvalidDestination(err error) bool { - type invalidDestination interface { - InvalidDestination() bool - } - te, ok := err.(invalidDestination) - return ok && te.InvalidDestination() -} - -// IsErrInsufficientBalance is a helper method for determining if an error indicates insufficient balance -func IsErrInsufficientBalance(err error) bool { - type insufficientBalance interface { - InsufficientBalance() bool - } - te, ok := err.(insufficientBalance) - return ok && te.InsufficientBalance() -} - -// IsErrUnauthorized is a helper method for determining if an error indicates the wallet unauthorized -func IsErrUnauthorized(err error) bool { - type unauthorized interface { - Unauthorized() bool - } - te, ok := err.(unauthorized) - return ok && te.Unauthorized() -} - -// IsErrInvalidSignature is a helper method for determining if an error indicates there was an invalid signature -func IsErrInvalidSignature(err error) bool { - type invalidSignature interface { - InvalidSignature() bool - } - te, ok := err.(invalidSignature) - return ok && te.InvalidSignature() -} - -// IsErrAlreadyExists is a helper method for determining if an error indicates the resource already exists -func IsErrAlreadyExists(err error) bool { - type alreadyExists interface { - AlreadyExistsError() bool - } - te, ok := err.(alreadyExists) - return ok && te.AlreadyExistsError() -} - -// IsErrForbidden is a helper method for determining if an error indicates the action is forbidden -func IsErrForbidden(err error) bool { - type forbidden interface { - ForbiddenError() bool - } - te, ok := err.(forbidden) - return ok && te.ForbiddenError() -} diff --git a/libs/formatters/cliformatter.go b/libs/formatters/cliformatter.go deleted file mode 100644 index c75cb5907..000000000 --- a/libs/formatters/cliformatter.go +++ /dev/null @@ -1,29 +0,0 @@ -package formatters - -import ( - "encoding/json" - - log "github.com/sirupsen/logrus" -) - -// CliFormatter is a logrus formatter which prints non-debug messages without tags and pretty-prints json -type CliFormatter struct { - log.TextFormatter -} - -// Format the log entry -func (f *CliFormatter) Format(entry *log.Entry) ([]byte, error) { - jmsg := json.RawMessage{} - err := json.Unmarshal([]byte(entry.Message), &jmsg) - if err == nil { - tmp, err := json.MarshalIndent(jmsg, "", " ") - if err == nil { - entry.Message = string(tmp) - } - } - - if entry.Level != log.DebugLevel { - return []byte(entry.Message), nil - } - return f.TextFormatter.Format(entry) -} diff --git a/libs/go.mod b/libs/go.mod deleted file mode 100644 index 3b90f185a..000000000 --- a/libs/go.mod +++ /dev/null @@ -1,107 +0,0 @@ -module github.com/brave-intl/bat-go/libs - -go 1.18 - -require ( - github.com/alicebob/miniredis/v2 v2.23.0 - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d - github.com/aws/aws-sdk-go v1.44.206 - github.com/aws/aws-sdk-go-v2 v1.18.0 - github.com/aws/aws-sdk-go-v2/config v1.17.10 - github.com/aws/aws-sdk-go-v2/credentials v1.12.23 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 - github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 - github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 - github.com/aws/smithy-go v1.13.5 - github.com/btcsuite/btcutil v1.0.2 - github.com/getsentry/sentry-go v0.14.0 - github.com/go-chi/chi v4.1.2+incompatible - github.com/go-jose/go-jose/v3 v3.0.0 - github.com/gofrs/uuid v4.0.0+incompatible - github.com/golang-migrate/migrate/v4 v4.15.2 - github.com/golang/mock v1.6.0 - github.com/gomodule/redigo v2.0.0+incompatible - github.com/google/go-querystring v1.1.0 - github.com/google/uuid v1.3.0 - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 - github.com/jmoiron/sqlx v1.3.5 - github.com/lib/pq v1.10.7 - github.com/linkedin/goavro v2.1.0+incompatible - github.com/mdlayher/vsock v1.2.0 - github.com/mssola/user_agent v0.5.3 - github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.13.0 - github.com/redis/go-redis/v9 v9.3.0 - github.com/rs/zerolog v1.28.0 - github.com/satori/go.uuid v1.2.0 - github.com/segmentio/kafka-go v0.4.35 - github.com/shengdoushi/base58 v1.0.0 - github.com/shopspring/decimal v1.3.1 - github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.1 - github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5 - github.com/throttled/throttled v2.2.5+incompatible - github.com/tyler-smith/go-bip39 v1.1.0 - golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.16.0 - golang.org/x/term v0.13.0 - gotest.tools v2.2.0+incompatible -) - -require ( - github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/containerd/containerd v1.6.18 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.2.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/klauspost/compress v1.15.15 // indirect - github.com/kr/pretty v0.3.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2 // indirect - github.com/pierrec/lz4/v4 v4.1.17 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/rogpeppe/go-internal v1.6.2 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/x448/float16 v0.8.4 // indirect - github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect - go.uber.org/atomic v1.10.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/linkedin/goavro.v1 v1.0.5 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/libs/go.sum b/libs/go.sum deleted file mode 100644 index 927fe8aa0..000000000 --- a/libs/go.sum +++ /dev/null @@ -1,2049 +0,0 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/spanner v1.28.0/go.mod h1:7m6mtQZn/hMbMfx62ct5EWrGND4DNqkXyrmBPRS+OJo= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/alicebob/miniredis/v2 v2.23.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= -github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.44.206 h1:xC7O40wdnKH4A95KdYt+smXl9hig1vu9b3mFxAxUoak= -github.com/aws/aws-sdk-go v1.44.206/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0= -github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 h1:RKci2D7tMwpvGpDNZnGQw9wk6v7o/xSwFcUAuNPoB8k= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9/go.mod h1:vCmV1q1VK8eoQJ5+aYE7PkK1K6v41qJ5pJdK3ggCDvg= -github.com/aws/aws-sdk-go-v2/config v1.6.0/go.mod h1:TNtBVmka80lRPk5+S9ZqVfFszOQAGJJ9KbT3EM3CHNU= -github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/config v1.17.10 h1:zBy5QQ/mkvHElM1rygHPAzuH+sl8nsdSaxSWj0+rpdE= -github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= -github.com/aws/aws-sdk-go-v2/credentials v1.3.2/go.mod h1:PACKuTJdt6AlXvEq8rFI4eDmoqDFC5DpVKQbWysaDgM= -github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23 h1:LctvcJMIb8pxvk5hQhChpCu0WlU6oKQmcYb1HA4IZSA= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23/go.mod h1:0awX9iRr/+UO7OwRQFpV1hNtXxOVuehpjVEzrIAYNcA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.4.0/go.mod h1:Mj/U8OpDbcVcoctrYwA2bak8k/HFPdcLzI/vaiXMwuM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 h1:E3PXZSI3F2bzyj6XxUXdTIfvp425HHhwKsFvmzBwHgs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.4.0/go.mod h1:eHwXu2+uE/T6gpnYWwBwqoeqRf9IXyCcolyOWDRAErQ= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.4/go.mod h1:Ex7XQmbFmgFHrjUX6TN3mApKW5Hglyga+F7wZHTtYhA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.0/go.mod h1:Q5jATQc+f1MfZp3PDMhn6ry18hGvE0i8yvbXoKbnZaE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 h1:Mza+vlnZr+fPKFKRq/lKGVvM6B/8ZZmNdEopOwSQLms= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 h1:2EXB7dtGwRYIN3XQ9qwIW504DVbKIw3r89xQnonGdsQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16/go.mod h1:XH+3h395e3WVdd6T2Z3mPxuI+x/HVtdqVOREkTiyubs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.2.2/go.mod h1:EASdTcM1lGhUe1/p4gkojHwlGJkeoRjjr1sRCzup3Is= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 h1:dpiPHgmFstgkLG07KaYAewvuptq5kvo52xn7tVSrtrQ= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10/go.mod h1:9cBNUHI2aW4ho0A5T87O294iPDuuUOSIEDjnd1Lq/z0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 h1:KSvtm1+fPXE0swe9GPjc6msyrdTT0LB/BP8eLugL1FI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20/go.mod h1:Mp4XI/CkWGD79AQxZ5lIFlgvC0A+gl+4BmyG1F+SfNc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.2/go.mod h1:NXmNI41bdEsJMrD0v9rUvbGCB5GwdBEpKvUvIY3vTFg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 h1:GE25AWCdNUPh9AOJzI9KIJnja7IwUc1WyUqz/JTyJ/I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.5.2/go.mod h1:QuL2Ym8BkrLmN4lUofXYq6000/i5jPjosCNK//t6gak= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.2/go.mod h1:np7TMuJNT83O0oDOSF8i4dF3dvGqA6hPYYo6YYkzgRA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 h1:piDBAaWkaxkkVV3xJJbTehXCZRXYs49kvpi/LG6LR2o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19/go.mod h1:BmQWRVkLTmyNzYPFAZgon53qKLWBNSvonugD1MrSWUs= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.12.0/go.mod h1:6J++A5xpo7QDsIeSqPK4UHqMSyPOCopa+zKtqAMhqVQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.16.1/go.mod h1:CQe/KvWV1AqRc65KqeJjrLzr5X2ijnFTTVzJW0VBRCI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 h1:/EMdFPW/Ppieh0WUtQf1+qCGNLdsq5UWUyevBQ6vMVc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1/go.mod h1:/NHbqPRiwxSPVOB2Xr+StDEH+GWV/64WwnUjv4KYzV0= -github.com/aws/aws-sdk-go-v2/service/sso v1.3.2/go.mod h1:J21I6kF+d/6XHVk7kp/cx9YVD2TMD2TbLwtRGVcinXo= -github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 h1:GFZitO48N/7EsFDt8fMa5iYdmWqkUDDB3Eje6z3kbG0= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 h1:jcw6kKZrtNfBPJkaHrscDOZoe5gvi9wjudnxvozYFJo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= -github.com/aws/aws-sdk-go-v2/service/sts v1.6.1/go.mod h1:hLZ/AnkIKHLuPGjEiyghNEdvJ2PP0MgOxcmv9EBJ4xs= -github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 h1:KRAix/KHvjGODaHAMXnxRk9t0D+4IJVUuS/uwXxngXk= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= -github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns= -github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhui/dktest v0.3.10 h1:0frpeeoM9pHouHjhLeZDuDTJ0PqjDTrycaHaMmkJAo8= -github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.13+incompatible h1:5s7uxnKZG+b8hYWlPYUi6x1Sjpq2MSt96d15eLZeHyw= -github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc= -github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linkedin/goavro v2.1.0+incompatible h1:DV2aUlj2xZiuxQyvag8Dy7zjY69ENjS66bWkSfdpddY= -github.com/linkedin/goavro v2.1.0+incompatible/go.mod h1:bBCwI2eGYpUI/4820s67MElg9tdeLbINjLjiM2xZFYM= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= -github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578= -github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= -github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/segmentio/kafka-go v0.4.35 h1:TAsQ7q1SjS39PcFvU0zDJhCuVAxHomy7xOAfbdSuhzs= -github.com/segmentio/kafka-go v0.4.35/go.mod h1:GAjxBQJdQMB5zfNA21AhpaqOB2Mu+w3De4ni3Gbm8y0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5 h1:UOKr78K7smMjkKuwZjbOCzIFTUhIUzXc5+tYeaPRirE= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5/go.mod h1:asllDl3XCEQ1lFPD4Y1juAn5p35Zd4ghNGcDKN7U7dU= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= -github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= -github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210818153620-00dd8d7831e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/linkedin/goavro.v1 v1.0.5 h1:BJa69CDh0awSsLUmZ9+BowBdokpduDZSM9Zk8oKHfN4= -gopkg.in/linkedin/goavro.v1 v1.0.5/go.mod h1:Aw5GdAbizjOEl0kAMHV9iHmA8reZzW/OKuJAl4Hb9F0= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= -gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= -modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= -modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= -modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= -modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= -modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= -modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/libs/handlers/handlers.go b/libs/handlers/handlers.go deleted file mode 100644 index f19f0ea3c..000000000 --- a/libs/handlers/handlers.go +++ /dev/null @@ -1,161 +0,0 @@ -package handlers - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - "strings" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/requestutils" - sentry "github.com/getsentry/sentry-go" - "github.com/rs/zerolog" -) - -// AppError is error type for json HTTP responses -type AppError struct { - Cause error `json:"-"` - Message string `json:"message"` // description of failure - ErrorCode string `json:"errorCode,omitempty"` // short error code string - Code int `json:"code"` // status code for some reason - Data interface{} `json:"data,omitempty"` // application specific data -} - -// Error makes app error an error -func (e AppError) Error() string { - msg := fmt.Sprintf("error: %s", e.Message) - if e.Cause != nil { - msg = fmt.Sprintf("%s: %s", msg, e.Cause) - } - return msg -} - -// ServeHTTP responds according to the passed AppError -func (e AppError) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - w.WriteHeader(e.Code) - if err := json.NewEncoder(w).Encode(e); err != nil { - panic(err) - } -} - -// WrapError with an additional message as an AppError -func WrapError(err error, msg string, passedCode int) *AppError { - // FIXME err should probably be first - // appErr, ok := err.(*AppError) - var appErr *AppError - if !errors.As(err, &appErr) { - code := passedCode - if code == 0 { - code = http.StatusBadRequest - } - // use defaults passed in - return &AppError{ - Cause: err, - Message: msg, - Code: code, - } - } - code := appErr.Code - if code == 0 { - code = passedCode - } - if len(msg) != 0 { - msg = fmt.Sprintf("%s: ", msg) - } - return &AppError{ - Cause: appErr.Cause, - Message: fmt.Sprintf("%s%s", msg, appErr.Message), - Code: code, - Data: appErr.Data, - } -} - -// RenderContent based on the header -func RenderContent(ctx context.Context, v interface{}, w http.ResponseWriter, status int) *AppError { - switch w.Header().Get("content-type") { - case "application/json": - var b bytes.Buffer - - if err := json.NewEncoder(&b).Encode(v); err != nil { - return WrapError(err, "Error encoding JSON", http.StatusInternalServerError) - } - - w.WriteHeader(status) - _, err := w.Write(b.Bytes()) - // Should never happen :fingers_crossed: - if err != nil { - return WrapError(err, "Error writing a response", http.StatusInternalServerError) - } - } - - return nil -} - -// WrapValidationError from govalidator -func WrapValidationError(err error) *AppError { - return ValidationError("request body", govalidator.ErrorsByField(err)) -} - -// CodedValidationError creates an error to communicate a bad request was formed -func CodedValidationError(message string, errorCode string, validationErrors interface{}) *AppError { - return &AppError{ - Message: "Error validating " + message, - ErrorCode: errorCode, - Code: http.StatusBadRequest, - Data: map[string]interface{}{ - "validationErrors": validationErrors, - }, - } -} - -// ValidationError creates an error to communicate a bad request was formed -func ValidationError(message string, validationErrors interface{}) *AppError { - return &AppError{ - Message: "Error validating " + message, - Code: http.StatusBadRequest, - Data: map[string]interface{}{ - "validationErrors": validationErrors, - }, - } -} - -// AppHandler is an http.Handler with JSON requests / responses -type AppHandler func(http.ResponseWriter, *http.Request) *AppError - -// ServeHTTP responds via the passed handler and handles returned errors -func (fn AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if strings.Contains(r.Header.Get("Accept"), "application/json") || - strings.Contains(r.Header.Get("Accept"), "*/*") || r.Header.Get("Accept") == "" { - w.Header().Set("content-type", "application/json") - } else { - w.WriteHeader(http.StatusBadRequest) - // return a 400 error here as we cannot supply the encoding type the client is asking for - } - - if e := fn(w, r); e != nil { - if e.Code >= 500 && e.Code <= 599 { - sentry.WithScope(func(scope *sentry.Scope) { - scope.SetTags(map[string]string{ - "reqID": requestutils.GetRequestID(r.Context()), - }) - sentry.CaptureException(e) - }) - } - - l := zerolog.Ctx(r.Context()) - l.UpdateContext(func(c zerolog.Context) zerolog.Context { - return c.Err(e) - }) - - if e.Cause != nil { - // Combine error with message - e.Message = fmt.Sprintf("%s: %v", e.Message, e.Cause) - } - - e.ServeHTTP(w, r) - } -} diff --git a/libs/handlers/handlers_test.go b/libs/handlers/handlers_test.go deleted file mode 100644 index be2de3d71..000000000 --- a/libs/handlers/handlers_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package handlers - -import ( - "errors" - "net/http" - "testing" -) - -func TestWrapError(t *testing.T) { - originatingError := errors.New("originating error") - err := WrapError(originatingError, "wrapping message", http.StatusInternalServerError) - if got, want := err.Code, http.StatusInternalServerError; got != want { - t.Fatalf("AppError holds error code creation stable, got = %v, want = %v", got, want) - } - if got, want := err.Message, "wrapping message"; got != want { - t.Fatalf("AppError holds error message creation stable, got = %v, want = %v", got, want) - } - if got, want := err.Cause, originatingError; got != want { - t.Fatalf("AppError holds error pointer stable, got = %v, want = %v", got, want) - } - - appErr := &AppError{ - Code: 1, - } - err = WrapError(appErr, "", 0) - if got, want := err.Code, 1; got != want { - t.Fatalf("AppError.Code should be original error value got %v, want %v", got, want) - } - - appErr = &AppError{} - err = WrapError(appErr, "", 5) - if got, want := err.Code, 5; got != want { - t.Fatalf("AppError.Code should be provided default value got %v, want %v", got, want) - } - - appErr = &AppError{ - Message: "a", - } - err = WrapError(appErr, "b", 0) - if got, want := err.Message, "b: a"; got != want { - t.Fatalf("AppError.Message wraps error messages got %v, want %v", got, want) - } - if got, want := err.Error(), "error: b: a"; got != want { - t.Fatalf("AppError.Message wraps error messages got %v, want %v", got, want) - } - err = WrapError(err, "c", 0) - if got, want := err.Message, "c: b: a"; got != want { - t.Fatalf("AppError.Error() wraps error messages recursively got %v, want %v", got, want) - } - if got, want := err.Error(), "error: c: b: a"; got != want { - t.Fatalf("AppError.Error() wraps error messages recursively got %v, want %v", got, want) - } - - appErr = &AppError{ - Message: "start", - Cause: errors.New("because"), - } - if got, want := appErr.Error(), "error: start: because"; got != want { - t.Fatalf("AppError.Error() wraps error messages and appends the cause to the end got %v, want %v", got, want) - } - err = WrapError(nil, "does not have to be passed", 403) - if got, want := err.Message, "does not have to be passed"; got != want { - t.Fatalf("AppError does not need to be passed") - } - if got, want := err.Error(), "error: does not have to be passed"; got != want { - t.Fatalf("AppError.Error() wraps error messages can stand alone got %v, want %v", got, want) - } -} diff --git a/libs/handlers/healthcheck.go b/libs/handlers/healthcheck.go deleted file mode 100644 index 2fcece34b..000000000 --- a/libs/handlers/healthcheck.go +++ /dev/null @@ -1,45 +0,0 @@ -package handlers - -import ( - "net/http" -) - -// HealthCheckResponseData - response structure for healthchecks -type HealthCheckResponseData struct { - BuildTime string `json:"buildTime"` - Commit string `json:"commit"` - Version string `json:"version"` - // service status is an accumulated map of service health structures mapped on service name - ServiceStatus map[string]interface{} `json:"serviceStatus,omitempty"` -} - -// HealthCheckResponse - response structure for healthchecks -type HealthCheckResponse struct { - Data HealthCheckResponseData `json:"data"` -} - -// HealthCheckHandler - function which generates a health check http.HandlerFunc -func HealthCheckHandler(version, buildTime, commit string, serviceStatus map[string]interface{}, check func() error) http.HandlerFunc { - return AppHandler( - func(w http.ResponseWriter, r *http.Request) *AppError { - var ctx = r.Context() - hcr := HealthCheckResponse{Data: HealthCheckResponseData{ - Commit: commit, - BuildTime: buildTime, - Version: version, - ServiceStatus: serviceStatus, - }} - - if check != nil { - if err := check(); err != nil { - return &AppError{ - Message: "service is (partially) unavailable", - Code: 500, - Data: hcr.Data, - } - } - } - - return RenderContent(ctx, hcr, w, http.StatusOK) - }).ServeHTTP -} diff --git a/libs/httpsignature/algorithm.go b/libs/httpsignature/algorithm.go deleted file mode 100644 index 32c551154..000000000 --- a/libs/httpsignature/algorithm.go +++ /dev/null @@ -1,53 +0,0 @@ -package httpsignature - -import ( - "errors" -) - -// Algorithm is an enum-like representing an algorithm that can be used for http signatures -type Algorithm int - -const ( - invalid Algorithm = iota - // ED25519 EdDSA - deprecated, all algorithm strings should be replaced with HS2019 - ED25519 - // HS2019 is a catch-all value for all algorithms - HS2019 - // AWSNITRO uses AWS nitro attesation functionality - AWSNITRO -) - -var algorithmName = map[Algorithm]string{ - ED25519: "ed25519", - HS2019: "hs2019", - AWSNITRO: "awsnitro", -} - -var algorithmID = map[string]Algorithm{ - "ed25519": ED25519, - "hs2019": HS2019, - "awsnitro": AWSNITRO, -} - -func (a Algorithm) String() string { - return algorithmName[a] -} - -// MarshalText marshalls the algorithm into text. -func (a *Algorithm) MarshalText() (text []byte, err error) { - if *a == invalid { - return nil, errors.New("not a supported algorithm") - } - text = []byte(a.String()) - return -} - -// UnmarshalText unmarshalls the algorithm from text. -func (a *Algorithm) UnmarshalText(text []byte) (err error) { - var exists bool - *a, exists = algorithmID[string(text)] - if !exists { - return errors.New("not a supported algorithm") - } - return nil -} diff --git a/libs/httpsignature/att_doc_sample.bin b/libs/httpsignature/att_doc_sample.bin deleted file mode 100644 index e8dc09f2e502965d26689c110c3e699163334ebc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4396 zcmd6pc~lek7RQrG2w{;$3Mh!Uun8pmvL`Aio2XP#F%VE6CX)#SLI@C4XjOugT@+D4 z6b#}H3W8AepeR)8j@l|ht%$pTYY-ozZ6_iY3ch!8PTxDtIrGQ;WxkV}xu5U7H`%Kg z%buhRGCF-g92q5ylOQ2t;VOGE35b{?wty-^*h~S9&7i>~L<$26n?+?)XjDFf&lFH$ zkx(3p#L7kfv)yTIx>zobL?eEr%o2maraG9grwC)2QV6bu$oIC%_TSENz6_vYhyfNoN5 zB3rHpAMRl~x!MwHZ`Rbq=VBvrTy>sI&wTBd+c~*2^+-SrPx>~?BRy#DWi8DUBWNzs z)?7l=T%x17L|1c(p5_vL%_XBWml$X+G1OcV0V6STu}BQ_gxvbW5dMK zP<#mmm~FFiL%*t50x`ola+0Z`$RsbV{_Q#hBVF_AJ3Or^KPJqE*tPVYJ!9Ka(iF3m@hsp@b zsXddSeXO#<)^x93dd4V=-~8;tyWZZoU}$hVrD40ZPe6iv>8cA$GsnESWIy|D!Teq2 z_2v~f4wYu*W_LJkli5#~9kNc|j} z$3cYCgDZ^-#Q-4$f?4w%DISg9r07?(cX!L8!M#S0vv;rjcszYe`Q?|*Y#Lqb!z*g) zmeLdqt*$7_2Lb_WwL?P|lvnS#ZQHc%3As+ySa@dJBM;hlNrpwKVa-=8jzPxdY|^rT z9RWQvuHNQ_!F)lSR474`gCJ8BLt~JH-P(_$F{YOi7iPJ!yxnTnrK1lX|2trChG5Rn z%KjI|EWz0Rl8qjjA_|8BKF%ICV4xJy`$IZa9sZp^^@Y;EhZcA4C7w}*$?g@jZag)M zP2ChLP51r1C#EIc%Xh)ThxFY?dVaZox|U}_{r<=bZG$_bZx+nxm}n__yr^mQwO@5Y zqD*An=0wky+eX-l51L@CgO126^?bY_8O%F7qr1cd%mjv?ORoiRC;`AqePDn$!+*8W zwt7^Hme1p@4O`#E6gF);(B`}Kqm8W$)u^C+U{eOaeViBrL=Xj2veqT1`25L#Jw7J* z)-3-z7nf~~^Xy#$9}r}@auTjKtuwT zh|Z@mfnaER_-l{*0y2I3ysPG&Y4JPKQr$gy*LNP9`#R>9t1=pU%V>cqExakw2X`{K z4!iOU_mu}aP+!={+!Av3)y-CBpR^M8ZAeZGs9x8~e8ReyO15U zgO@zfE6j`^29`wqJZn4xV8B;~D3_%y36slZQ(at8uabs#d=eZZA;;3l{Eve~?PWMB z(gi(q;S1;-CV*iQOUR^==v0A_#6dVB5`#mdv0(v|C1TUb=#7J6!G&P}f->1Gk@eN? zZwz1gAV(@XRF_d2KeNjsbSL+zuW(gs#Y^djTmrrD3@sql?!K_svZxFK^RtgpT6KvN zi`-MmGre;C?0;An)#4Rsye?o>a4DWRT~g)0)Kuva(om7nIkosVd=PF7NWy)EQoMbD z;tj;}T-PqIYi43hM(Bew^5De&PiPGQ&Z4jwfWiI*IH01_6rj@-DAFm@!PI_gX(DuA z2O>V50$>phrm@KABXQ#pY)PN@AkuJGs2%b7 z`w_fB=dgq=$Ef63osaQ$Ly{BZ zw8k!>`RsAnCp)~iy&n2W#+VjSXjUSI+Mku#zHXfm{Gt#7UOpYYZe1P0Kj<91qSq~4 zm?z9A%l+UrfoxJam#E`UamXa)hu&?IVA59k#k##a83ZMxwn#)Tt$u5mVxE=;1TWql z(HU{l=C(Vp($FT@=J*hM{FA^OFcBM20bqcE9hCDa{O2vrUl=HvOE*v0e=0|j-$hRK zHVzC~J*%wqaI|NVetVkZ;fAtE2JgoY)_EDo>gA;s(fGyXK27t|Z`rKuEo>8VgKpJD zbR)q0YEflzTf6&s7uN-aWs9!m@1C>1c8#j2fN^i#`w}}~GlGvWLwx|@2N!x^&v7B(<;@;W|kjfUQ@rN{sKNU?^3$`ed=1Yr=MeP zKUMIispek#?wahDD$kBR$xY*E#y)n_4l0?}-=DDDEYJJd%y#VwWf8~2WZbTBS)4#3 zhC?Ed_#SawED{qUsDob~&c#HB^9{sY5Yzd`^2 diff --git a/libs/httpsignature/ed25519.go b/libs/httpsignature/ed25519.go deleted file mode 100644 index 464072400..000000000 --- a/libs/httpsignature/ed25519.go +++ /dev/null @@ -1,34 +0,0 @@ -package httpsignature - -import ( - "crypto" - "encoding/hex" - "errors" - "io" - "strconv" - - "golang.org/x/crypto/ed25519" -) - -// Ed25519PubKey a wrapper type around ed25519.PublicKey to fulfill interface Verifier -type Ed25519PubKey ed25519.PublicKey - -// Verify the signature sig for message using the ed25519 public key pk -// Returns true if the signature is valid, false if not and error if the key provided is not valid -func (pk Ed25519PubKey) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) { - if l := len(pk); l != ed25519.PublicKeySize { - return false, errors.New("ed25519: bad public key length: " + strconv.Itoa(l)) - } - - return ed25519.Verify(ed25519.PublicKey(pk), message, sig), nil -} - -func (pk Ed25519PubKey) String() string { - return hex.EncodeToString(pk) -} - -// GenerateEd25519Key generate an ed25519 keypair and return it -func GenerateEd25519Key(rand io.Reader) (Ed25519PubKey, ed25519.PrivateKey, error) { - publicKey, privateKey, err := ed25519.GenerateKey(nil) - return Ed25519PubKey(publicKey), privateKey, err -} diff --git a/libs/httpsignature/encapsulate.go b/libs/httpsignature/encapsulate.go deleted file mode 100644 index 14a74f636..000000000 --- a/libs/httpsignature/encapsulate.go +++ /dev/null @@ -1,151 +0,0 @@ -package httpsignature - -import ( - "bytes" - "context" - "errors" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "strings" - - "github.com/brave-intl/bat-go/libs/requestutils" - "golang.org/x/net/http/httpguts" -) - -// HTTPSignedRequest encapsulates a signed HTTP request -type HTTPSignedRequest struct { - Headers map[string]string `json:"headers" valid:"-"` - Body string `json:"octets" valid:"json"` -} - -// Extract from the encapsulated signed request -// into the provided HTTP request -func (sr *HTTPSignedRequest) Extract(r *http.Request) (*SignatureParams, error) { - if r == nil { - return nil, errors.New("r was nil") - } - - r.Body = ioutil.NopCloser(bytes.NewBufferString(sr.Body)) - r.ContentLength = int64(len(sr.Body)) - if r.Header == nil { - r.Header = http.Header{} - } - for k, v := range sr.Headers { - if k == RequestTargetHeader { - method, uri, found := strings.Cut(v, " ") - if !found { - return nil, errors.New("invalid encapsulated (request-target) pseudo-header value") - } - r.Method = strings.ToUpper(method) - pURI, err := url.ParseRequestURI(uri) - if err != nil { - return nil, fmt.Errorf("invalid encapsulated (request-target) pseudo-header value: %e", err) - } - r.URL = pURI - } else { - if !httpguts.ValidHeaderFieldName(k) { - return nil, errors.New("invalid encapsulated header name") - } - if !httpguts.ValidHeaderFieldValue(v) { - return nil, errors.New("invalid encapsulated header value") - } - - r.Header.Set(k, v) - } - } - - return SignatureParamsFromRequest(r) -} - -// EncapsulateRequest a signed HTTP request -func EncapsulateRequest(req *http.Request) (*HTTPSignedRequest, error) { - s, err := SignatureParamsFromRequest(req) - if err != nil { - return nil, err - } - - enc := HTTPSignedRequest{} - enc.Headers = make(map[string]string) - for _, k := range s.Headers { - if k == RequestTargetHeader { - enc.Headers[k] = formatRequestTarget(req) - } else { - values := req.Header[http.CanonicalHeaderKey(k)] - enc.Headers[k] = strings.Join(values, ", ") - } - } - enc.Headers["signature"] = req.Header.Get("signature") - - bodyBytes, err := requestutils.Read(req.Context(), req.Body) - if err != nil { - return nil, err - } - enc.Body = string(bodyBytes) - return &enc, nil -} - -// HTTPSignedResponse encapsulates a signed HTTP response -type HTTPSignedResponse struct { - StatusCode int `json:"statusCode" valid:"-"` - Headers map[string]string `json:"headers" valid:"-"` - Body string `json:"octets" valid:"json"` -} - -// Extract from the encapsulated signed response -// into the provided HTTP response -func (sr *HTTPSignedResponse) Extract(r *http.Response) (*SignatureParams, error) { - if r == nil { - return nil, errors.New("r was nil") - } - - r.StatusCode = sr.StatusCode - r.Body = ioutil.NopCloser(bytes.NewBufferString(sr.Body)) - if r.Header == nil { - r.Header = http.Header{} - } - for k, v := range sr.Headers { - if !httpguts.ValidHeaderFieldName(k) { - return nil, errors.New("invalid encapsulated header name") - } - if !httpguts.ValidHeaderFieldValue(v) { - return nil, errors.New("invalid encapsulated header value") - } - - if k == RequestTargetHeader { - // TODO implement pseudo-header - return nil, fmt.Errorf("%s pseudo-header not implemented", RequestTargetHeader) - } - - r.Header.Set(k, v) - } - - return SignatureParamsFromResponse(r) -} - -// EncapsulateResponse a signed HTTP response -func EncapsulateResponse(ctx context.Context, resp *http.Response) (*HTTPSignedResponse, error) { - s, err := SignatureParamsFromResponse(resp) - if err != nil { - return nil, err - } - - enc := HTTPSignedResponse{} - enc.Headers = make(map[string]string) - for _, k := range s.Headers { - values := resp.Header[http.CanonicalHeaderKey(k)] - enc.Headers[k] = strings.Join(values, ", ") - } - enc.Headers["signature"] = resp.Header.Get("signature") - - // TODO implement pseudo-header - - bodyBytes, err := requestutils.Read(ctx, resp.Body) - if err != nil { - return nil, err - } - enc.Body = string(bodyBytes) - enc.StatusCode = resp.StatusCode - return &enc, nil -} diff --git a/libs/httpsignature/encapsulate_test.go b/libs/httpsignature/encapsulate_test.go deleted file mode 100644 index 380bf246f..000000000 --- a/libs/httpsignature/encapsulate_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package httpsignature - -import ( - "bytes" - "context" - "crypto" - "encoding/hex" - "io/ioutil" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEncapsulateRequest(t *testing.T) { - var pubKey Ed25519PubKey - pubKey, err := hex.DecodeString("e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d") - assert.NoError(t, err) - - sig := `keyId="primary",algorithm="ed25519",headers="digest foo",signature="HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA=="` - digest := "SHA-256=RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=" - body := []byte("{\"hello\": \"world\"}\n") - - r, err := http.NewRequest("GET", "http://example.org/foo", ioutil.NopCloser(bytes.NewBuffer(body))) - assert.NoError(t, err) - - r.Header.Set("Foo", "bar") - r.Header.Set("Digest", digest) - r.Header.Set("Signature", sig) - - // Header that does not need to be passed - r.Header.Set("Bar", "foo") - - er, err := EncapsulateRequest(r) - assert.NoError(t, err) - - expectedHeaders := map[string]string{ - "digest": digest, - "signature": sig, - "foo": "bar", - } - - assert.Equal(t, body, []byte(er.Body), "Encapsulated body should be identical") - assert.Equal(t, expectedHeaders, er.Headers, "Encapsulated headers should be identical") - - var r2 http.Request - sp, err := er.Extract(&r2) - assert.NoError(t, err) - sp.Algorithm = ED25519 - sp.KeyID = "primary" - sp.Headers = []string{"digest", "foo"} - - valid, err := sp.Verify(pubKey, crypto.Hash(0), &r2) - assert.NoError(t, err) - assert.Equal(t, true, valid, "The siganture should be valid after an encapsulation roundtrip") - - er.Body = "{\"world\": \"hello\"}\n" - - var r3 http.Request - _, err = er.Extract(&r3) - assert.NoError(t, err) - - valid, err = sp.Verify(pubKey, crypto.Hash(0), &r3) - assert.NoError(t, err) - assert.Equal(t, false, valid, "The siganture should be invalid since the body is different") -} - -func TestEncapsulateResponse(t *testing.T) { - var pubKey Ed25519PubKey - pubKey, err := hex.DecodeString("e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d") - assert.NoError(t, err) - - sig := `keyId="primary",algorithm="ed25519",headers="digest foo",signature="HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA=="` - digest := "SHA-256=RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=" - body := []byte("{\"hello\": \"world\"}\n") - - r := &http.Response{Header: http.Header{}} - r.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - - r.Header.Set("Foo", "bar") - r.Header.Set("Digest", digest) - r.Header.Set("Signature", sig) - - // Header that does not need to be passed - r.Header.Set("Bar", "foo") - - er, err := EncapsulateResponse(context.Background(), r) - assert.NoError(t, err) - - expectedHeaders := map[string]string{ - "digest": digest, - "signature": sig, - "foo": "bar", - } - - assert.Equal(t, body, []byte(er.Body), "Encapsulated body should be identical") - assert.Equal(t, expectedHeaders, er.Headers, "Encapsulated headers should be identical") - - var r2 http.Response - sp, err := er.Extract(&r2) - assert.NoError(t, err) - sp.Algorithm = ED25519 - sp.KeyID = "primary" - sp.Headers = []string{"digest", "foo"} - - valid, err := sp.VerifyResponse(pubKey, crypto.Hash(0), &r2) - assert.NoError(t, err) - assert.Equal(t, true, valid, "The siganture should be valid after an encapsulation roundtrip") - - er.Body = "{\"world\": \"hello\"}\n" - - var r3 http.Response - _, err = er.Extract(&r3) - assert.NoError(t, err) - - valid, err = sp.VerifyResponse(pubKey, crypto.Hash(0), &r3) - assert.NoError(t, err) - assert.Equal(t, false, valid, "The siganture should be invalid since the body is different") -} diff --git a/libs/httpsignature/hmac.go b/libs/httpsignature/hmac.go deleted file mode 100644 index a87156d08..000000000 --- a/libs/httpsignature/hmac.go +++ /dev/null @@ -1,39 +0,0 @@ -package httpsignature - -import ( - "crypto" - "crypto/hmac" - "crypto/sha512" - "crypto/subtle" - "io" -) - -// HMACKey is a symmetric key that can be used for HMAC-SHA512 request signing and verification -type HMACKey string - -// Sign the message using the hmac key -func (key HMACKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { - hhash := hmac.New(sha512.New, []byte(key)) - // writing the message (HTTP signing string) to it - _, err = hhash.Write(message) - if err != nil { - return nil, err - } - // Get the hash sum, do not base64 encode it since sig was decoded already - return hhash.Sum(nil), nil -} - -// Verify the signature sig for message using the hmac key -func (key HMACKey) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) { - hashSum, err := key.Sign(nil, message, nil) - if err != nil { - return false, err - } - // Return bool by checking whether or not the calculated hash is equal to - // sig pulled out of the header. Check if returned int is equal to 1 to return a bool - return subtle.ConstantTimeCompare(hashSum, sig) == 1, nil -} - -func (key HMACKey) String() string { - return string(key) -} diff --git a/libs/httpsignature/httpsignature.go b/libs/httpsignature/httpsignature.go deleted file mode 100644 index 9d3697ba8..000000000 --- a/libs/httpsignature/httpsignature.go +++ /dev/null @@ -1,441 +0,0 @@ -// Package httpsignature contains methods for signing and verifing HTTP requests per -// https://www.ietf.org/id/draft-cavage-http-signatures-08.txt -package httpsignature - -import ( - "bytes" - "context" - "crypto" - "crypto/rand" - "encoding/base64" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "regexp" - "strings" - - "github.com/brave-intl/bat-go/libs/digest" - "github.com/brave-intl/bat-go/libs/requestutils" -) - -// SignatureParams contains parameters needed to create and verify signatures -type SignatureParams struct { - Algorithm Algorithm - KeyID string - DigestAlgorithm *crypto.Hash // optional - Headers []string // optional -} - -// signature is an internal represention of an http signature and it's parameters -type signature struct { - SignatureParams - Sig string -} - -// Signator is an interface for cryptographic signature creation -// NOTE that this is a subset of the crypto.Signer interface -type Signator interface { - Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) -} - -// Verifier is an interface for cryptographic signature verification -type Verifier interface { - Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) - String() string -} - -// ParameterizedSignator contains the parameters / options needed to create signatures and a signator -type ParameterizedSignator struct { - SignatureParams - Signator Signator - Opts crypto.SignerOpts -} - -// ParameterizedSignatorResponseWriter wraps a response writer to sign the response automatically -type ParameterizedSignatorResponseWriter struct { - ParameterizedSignator - w http.ResponseWriter - statusCode int -} - -// Keystore provides a way to lookup a public key based on the keyID a request was signed with -type Keystore interface { - // LookupVerifier based on the keyID - LookupVerifier(ctx context.Context, keyID string) (context.Context, *Verifier, error) -} - -// StaticKeystore is a keystore that always returns a static verifier independent of keyID -type StaticKeystore struct { - Verifier -} - -// ParameterizedKeystoreVerifier is an interface for cryptographic signature verification -type ParameterizedKeystoreVerifier struct { - SignatureParams - Keystore Keystore - Opts crypto.SignerOpts -} - -const ( - // HostHeader is the host header - HostHeader = "host" - // DigestHeader is the header where a digest of the body will be stored - DigestHeader = "digest" - // RequestTargetHeader is a pseudo header consisting of the HTTP method and request uri - RequestTargetHeader = "(request-target)" -) - -var ( - signatureRegex = regexp.MustCompile(`(\w+)="([^"]*)"`) -) - -// LookupVerifier by returning a static verifier -func (sk *StaticKeystore) LookupVerifier(ctx context.Context, keyID string) (context.Context, *Verifier, error) { - return ctx, &sk.Verifier, nil -} - -// TODO Add New function -// NOTE New function should check that all added headers are lower-cased - -// IsMalformed returns true if the signature parameters are invalid -func (sp *SignatureParams) IsMalformed() bool { - if sp.Algorithm == invalid { - return true - } - for _, header := range sp.Headers { - if header != strings.ToLower(header) { - return true // all headers must be lower-cased - } - } - return false -} - -// BuildSigningString builds the signing string according to the SignatureParams s and -// HTTP request req -func (sp *SignatureParams) BuildSigningString(req *http.Request) (out []byte, err error) { - if req.Body != nil { - body, err := requestutils.Read(context.Background(), req.Body) - if err != nil { - return nil, err - } - req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - return sp.buildSigningString(body, req.Header, req) - } - return sp.buildSigningString(nil, req.Header, req) -} - -// BuildSigningStringForResponse builds the signing string according to the SignatureParams s and -// HTTP response resp -func (sp *SignatureParams) BuildSigningStringForResponse(resp *http.Response) (out []byte, err error) { - if resp.Body != nil { - body, err := requestutils.Read(context.Background(), resp.Body) - if err != nil { - return out, err - } - resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - return sp.buildSigningString(body, resp.Header, resp.Request) - } - return sp.buildSigningString(nil, resp.Header, resp.Request) -} - -func formatRequestTarget(req *http.Request) string { - return fmt.Sprintf("%s %s", strings.ToLower(req.Method), req.URL.RequestURI()) -} - -func (sp *SignatureParams) buildSigningString(body []byte, headers http.Header, req *http.Request) (out []byte, err error) { - if sp.IsMalformed() { - return nil, errors.New("refusing to build signing string with malformed params") - } - - signedHeaders := sp.Headers - if len(signedHeaders) == 0 { - signedHeaders = []string{"date"} - } - - for i, header := range signedHeaders { - if header == RequestTargetHeader { - if req == nil { - return nil, fmt.Errorf("request must be present to use the %s pseudo-header", RequestTargetHeader) - } - if req.URL != nil && len(req.Method) > 0 { - out = append(out, []byte(fmt.Sprintf("%s: %s", RequestTargetHeader, formatRequestTarget(req)))...) - } else { - return nil, fmt.Errorf("request must have a URL and Method to use the %s pseudo-header", RequestTargetHeader) - } - } else if header == DigestHeader { - // Just like before default to SHA256 - var d digest.Instance - d.Hash = crypto.SHA256 - - // If something else is set though use that hash instead - if sp.DigestAlgorithm != nil { - d.Hash = *sp.DigestAlgorithm - } - - if body != nil { - d.Update(body) - } - headers.Add("Digest", d.String()) - out = append(out, []byte(fmt.Sprintf("%s: %s", "digest", d.String()))...) - } else if header == HostHeader { - if req == nil { - return nil, fmt.Errorf("request must be present to use the Host header") - } - // in some environments it seems that the HostTransfer middleware correctly sets - // the Host header to the xforwardedhost value - host := headers.Get(requestutils.HostHeaderKey) - if host == "" { - host = req.Host - } else { - host = strings.Join(headers[http.CanonicalHeaderKey(header)], ", ") - } - out = append(out, []byte(fmt.Sprintf("%s: %s", "host", host))...) - } else { - headerToAppend := headers[http.CanonicalHeaderKey(header)] - if len(headerToAppend) < 1 { - return nil, fmt.Errorf("header %s was called for but is not present", header) - } - val := strings.Join(headerToAppend, ", ") - out = append(out, []byte(fmt.Sprintf("%s: %s", header, val))...) - } - - if i != len(sp.Headers)-1 { - out = append(out, byte('\n')) - } - } - return out, nil -} - -// Sign the included HTTP request req using signator and options opts -func (sp *SignatureParams) Sign(signator Signator, opts crypto.SignerOpts, req *http.Request) error { - ss, err := sp.BuildSigningString(req) - if err != nil { - return err - } - return sp.sign(signator, opts, ss, req.Header) -} - -// SignResponse using signator and options opts -func (sp *SignatureParams) SignResponse(signator Signator, opts crypto.SignerOpts, resp *http.Response) error { - ss, err := sp.BuildSigningStringForResponse(resp) - if err != nil { - return err - } - return sp.sign(signator, opts, ss, resp.Header) -} - -func (sp *SignatureParams) sign(signator Signator, opts crypto.SignerOpts, ss []byte, headers http.Header) error { - sig, err := signator.Sign(rand.Reader, ss, opts) - if err != nil { - return err - } - s := signature{ - SignatureParams: *sp, - Sig: base64.StdEncoding.EncodeToString(sig), - } - - sHeader, err := s.MarshalText() - if err != nil { - return err - } - headers.Set("Signature", string(sHeader)) - return nil -} - -// SignRequest using signator and options opts in the parameterized signator -func (p *ParameterizedSignator) SignRequest(req *http.Request) error { - return p.SignatureParams.Sign(p.Signator, p.Opts, req) -} - -// SignResponse using signator and options opts in the parameterized signator -func (p *ParameterizedSignator) SignResponse(resp *http.Response) error { - return p.SignatureParams.SignResponse(p.Signator, p.Opts, resp) -} - -// NewParameterizedSignatorResponseWriter wraps the provided response writer and signs the response -func NewParameterizedSignatorResponseWriter(p ParameterizedSignator, w http.ResponseWriter) *ParameterizedSignatorResponseWriter { - return &ParameterizedSignatorResponseWriter{ - ParameterizedSignator: p, - w: w, - statusCode: -1, - } -} - -// Header returns the header map that will be sent by -// WriteHeader. -func (psrw *ParameterizedSignatorResponseWriter) Header() http.Header { - return psrw.w.Header() -} - -// WriteHeader sends an HTTP response header with the provided -// status code. -func (psrw *ParameterizedSignatorResponseWriter) WriteHeader(statusCode int) { - psrw.statusCode = statusCode -} - -// Write writes the data to the connection as part of an HTTP reply. -// -// For the ResponseWriter, we also sign the response before writing it out -func (psrw *ParameterizedSignatorResponseWriter) Write(body []byte) (int, error) { - ss, err := psrw.SignatureParams.buildSigningString(body, psrw.Header(), nil) - if err != nil { - return -1, err - } - err = psrw.SignatureParams.sign(psrw.Signator, psrw.Opts, ss, psrw.Header()) - if err != nil { - return -1, err - } - - if psrw.statusCode != -1 { - psrw.w.WriteHeader(psrw.statusCode) - } - return psrw.w.Write(body) -} - -// Verify the HTTP signature s over HTTP request req using verifier with options opts -func (sp *SignatureParams) Verify(verifier Verifier, opts crypto.SignerOpts, req *http.Request) (bool, error) { - signingStr, err := sp.BuildSigningString(req) - if err != nil { - return false, err - } - return sp.verify(verifier, opts, signingStr, req.Header) -} - -// VerifyResponse by verifying the HTTP signature over HTTP response resp using verifier with options opts -func (sp *SignatureParams) VerifyResponse(verifier Verifier, opts crypto.SignerOpts, resp *http.Response) (bool, error) { - signingStr, err := sp.BuildSigningStringForResponse(resp) - if err != nil { - return false, err - } - return sp.verify(verifier, opts, signingStr, resp.Header) -} - -func (sp *SignatureParams) verify(verifier Verifier, opts crypto.SignerOpts, ss []byte, headers http.Header) (bool, error) { - var tmp signature - err := tmp.UnmarshalText([]byte(headers.Get("Signature"))) - if err != nil { - return false, err - } - - sig, err := base64.StdEncoding.DecodeString(tmp.Sig) - if err != nil { - return false, err - } - return verifier.Verify(ss, sig, opts) -} - -// VerifyRequest using keystore to lookup verifier with options opts -// returns the key id if the signature is valid and an error otherwise -func (pkv *ParameterizedKeystoreVerifier) VerifyRequest(req *http.Request) (context.Context, string, error) { - sp, err := SignatureParamsFromRequest(req) - if err != nil { - return nil, "", err - } - - ctx, verifier, err := pkv.Keystore.LookupVerifier(req.Context(), sp.KeyID) - if err != nil { - return nil, "", err - } - - if verifier == nil { - return nil, "", fmt.Errorf("no verifier matching keyId %s was found", sp.KeyID) - } - - // Override algorithm and headers to those we want to enforce - sp.Algorithm = pkv.SignatureParams.Algorithm - sp.Headers = pkv.SignatureParams.Headers - - valid, err := sp.Verify(*verifier, pkv.Opts, req) - if err != nil { - return nil, "", err - } - if !valid { - return nil, "", errors.New("signature is not valid") - } - - return ctx, sp.KeyID, nil -} - -// MarshalText marshalls the signature into text. -func (s *signature) MarshalText() (text []byte, err error) { - if s.IsMalformed() { - return nil, errors.New("not a valid Algorithm") - } - - algo, err := s.Algorithm.MarshalText() - if err != nil { - return nil, err - } - - headers := "" - if len(s.Headers) > 0 { - headers = fmt.Sprintf(",headers=\"%s\"", strings.Join(s.Headers, " ")) - } - - text = []byte(fmt.Sprintf("keyId=\"%s\",algorithm=\"%s\"%s,signature=\"%s\"", s.KeyID, algo, headers, s.Sig)) - return text, nil -} - -// UnmarshalText unmarshalls the signature from text. -func (s *signature) UnmarshalText(text []byte) (err error) { - if len(text) == 0 { - return errors.New("signature header is empty") - } - - var key string - var value string - - s.Algorithm = invalid - s.KeyID = "" - s.Sig = "" - - str := string(text) - for _, m := range signatureRegex.FindAllStringSubmatch(str, -1) { - key = m[1] - value = m[2] - - if key == "keyId" { - s.KeyID = value - } else if key == "algorithm" { - err := s.Algorithm.UnmarshalText([]byte(value)) - if err != nil { - return err - } - } else if key == "headers" { - s.Headers = strings.Split(value, " ") - } else if key == "signature" { - s.Sig = value - } else { - return errors.New("invalid key in signature") - } - } - - // Check that all required fields were present - if s.Algorithm == invalid || len(s.KeyID) == 0 || len(s.Sig) == 0 { - return errors.New("a valid signature MUST have algorithm, keyId, and signature keys") - } - - return nil -} - -// SignatureParamsFromRequest extracts the signature parameters from a signed http request -func SignatureParamsFromRequest(req *http.Request) (*SignatureParams, error) { - var s signature - err := s.UnmarshalText([]byte(req.Header.Get("Signature"))) - if err != nil { - return nil, err - } - return &s.SignatureParams, nil -} - -// SignatureParamsFromResponse extracts the signature parameters from a signed http response -func SignatureParamsFromResponse(resp *http.Response) (*SignatureParams, error) { - var s signature - err := s.UnmarshalText([]byte(resp.Header.Get("Signature"))) - if err != nil { - return nil, err - } - return &s.SignatureParams, nil -} diff --git a/libs/httpsignature/httpsignature_test.go b/libs/httpsignature/httpsignature_test.go deleted file mode 100644 index 6cb37932c..000000000 --- a/libs/httpsignature/httpsignature_test.go +++ /dev/null @@ -1,743 +0,0 @@ -package httpsignature - -import ( - "bytes" - "crypto" - "encoding/hex" - "io" - "io/ioutil" - "net/http" - "reflect" - "testing" - "time" - - "golang.org/x/crypto/ed25519" -) - -func TestBuildSigningString(t *testing.T) { - var s signature - s.Algorithm = ED25519 - s.KeyID = "Test" - s.Headers = []string{"(request-target)", "host", "date", "cache-control", "x-example"} - - r, err := http.NewRequest("GET", "http://example.org/foo", nil) - if err != nil { - t.Error(err) - } - r.Header.Set("Host", "example.org") - r.Header.Set("Date", "Tue, 07 Jun 2014 20:51:35 GMT") - - // FIXME Check how go parses headers in http server - //r.Header.Add("X-Example", "Example header\nwith some whitespace.") - r.Header.Add("X-Example", "Example header with some whitespace.") - - r.Header.Add("Cache-Control", "max-age=60") - r.Header.Add("Cache-Control", "must-revalidate") - - expected := "(request-target): get /foo\nhost: example.org\ndate: Tue, 07 Jun 2014 20:51:35 GMT\ncache-control: max-age=60, must-revalidate\nx-example: Example header with some whitespace." - - res, err := s.BuildSigningString(r) - if err != nil { - t.Error(err) - t.Error("Unexpected error while building signing string") - } - if string(res) != expected { - t.Error(string(res)) - } - - // TODO add test to cover multiple headers with different capitalization - // TODO add test covering request uri with query parameters - // TODO add test covering no headers (date only) -} - -func TestSign(t *testing.T) { - // ED25519 Test - var privKey ed25519.PrivateKey - privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" - privKey, err := hex.DecodeString(privHex) - if err != nil { - t.Error(err) - } - - var s signature - s.Algorithm = ED25519 - s.KeyID = "primary" - s.Headers = []string{"foo"} - - r, err := http.NewRequest("GET", "http://example.org/foo", nil) - if err != nil { - t.Error(err) - } - r.Header.Set("Foo", "bar") - - err = s.Sign(privKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building ED25519 signing string:", err) - } - - err = s.UnmarshalText([]byte(r.Header.Get("Signature"))) - if err != nil { - t.Error(err) - } - - if s.Sig != "RbGSX1MttcKCpCkq9nsPGkdJGUZsAU+0TpiXJYkwde+0ZwxEp9dXO3v17DwyGLXjv385253RdGI7URbrI7J6DQ==" { - t.Error("Incorrect signature genearted for ED25519") - } -} - -func TestSignRequest(t *testing.T) { - // ED25519 Test - var privKey ed25519.PrivateKey - privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" - privKey, err := hex.DecodeString(privHex) - if err != nil { - t.Error(err) - } - - var sp SignatureParams - sp.Algorithm = ED25519 - sp.KeyID = "primary" - sp.Headers = []string{"foo"} - - ps := ParameterizedSignator{ - SignatureParams: sp, - Signator: privKey, - Opts: crypto.Hash(0), - } - - r, err := http.NewRequest("GET", "http://example.org/foo", nil) - if err != nil { - t.Error(err) - } - r.Header.Set("Foo", "bar") - - err = ps.SignRequest(r) - if err != nil { - t.Error("Unexpected error while building ED25519 signing string:", err) - } - - var s signature - err = s.UnmarshalText([]byte(r.Header.Get("Signature"))) - if err != nil { - t.Error(err) - } - - if s.Sig != "RbGSX1MttcKCpCkq9nsPGkdJGUZsAU+0TpiXJYkwde+0ZwxEp9dXO3v17DwyGLXjv385253RdGI7URbrI7J6DQ==" { - t.Error("Incorrect signature genearted for ED25519") - } - - // HS2019 Test (HMAC-SHA-512) - var sp2 SignatureParams - sp2.Algorithm = HS2019 - sp2.KeyID = "secondary" - sp2.Headers = []string{"(request-target)", "foo"} - - ps2 := ParameterizedSignator{ - SignatureParams: sp2, - Signator: HMACKey(privHex), - Opts: crypto.Hash(0), - } - - r2, reqErr := http.NewRequest("GET", "http://example.org/foo2", nil) - if reqErr != nil { - t.Error(reqErr) - } - r2.Header.Set("Foo", "bar") - - signErr := ps2.SignRequest(r2) - if signErr != nil { - t.Error("Unexpected error while building HS2019 signing string:", signErr) - } - - var s2 signature - err = s2.UnmarshalText([]byte(r2.Header.Get("Signature"))) - if err != nil { - t.Error(err) - } - - // Value generated using https://dinochiesa.github.io/httpsig/ - if s2.Sig != "q4hNevLfEiHZVCNUCkfxv89YFdpujD3FHfQUQSRnZPmRnakArWlv/KQRsRvmxL9xamS68KePztm1O+CvjIoX1Q==" { - t.Error("Incorrect signature generated for HS2019") - } - - // body signing test - var sp3 SignatureParams - sp3.Algorithm = ED25519 - sp3.KeyID = "primary" - sp3.Headers = []string{"digest", "foo"} - body := []byte("{\"hello\": \"world\"}\n") - - ps3 := ParameterizedSignator{ - SignatureParams: sp3, - Signator: privKey, - Opts: crypto.Hash(0), - } - - r, err = http.NewRequest("GET", "http://example.org/foo", ioutil.NopCloser(bytes.NewBuffer(body))) - if err != nil { - t.Error(err) - } - r.Header.Set("Foo", "bar") - - err = ps3.SignRequest(r) - if err != nil { - t.Error("Unexpected error while building ED25519 signing string:", err) - } - - if r.Header.Get("Digest") != "SHA-256=RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=" { - t.Error("Incorrect digest generated for '{\"hello\", \"world\"}\\n'") - } - - var s3 signature - err = s3.UnmarshalText([]byte(r.Header.Get("Signature"))) - if err != nil { - t.Error(err) - } - - if s3.Sig != "HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA==" { - t.Error("Incorrect signature genearted for ED25519") - } -} - -func TestVerify(t *testing.T) { - var pubKey Ed25519PubKey - pubKey, err := hex.DecodeString("e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d") - if err != nil { - t.Error(err) - } - - var s signature - s.Algorithm = ED25519 - s.KeyID = "primary" - s.Headers = []string{"foo"} - s.Sig = "RbGSX1MttcKCpCkq9nsPGkdJGUZsAU+0TpiXJYkwde+0ZwxEp9dXO3v17DwyGLXjv385253RdGI7URbrI7J6DQ==" - - r, err := http.NewRequest("GET", "http://example.org/foo", nil) - if err != nil { - t.Error(err) - } - - r.Header.Set("Foo", "bar") - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest",signature="`+s.Sig+`"`) - - valid, err := s.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if !valid { - t.Error("The signature should be valid") - } - - s.Sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest",signature="`+s.Sig+`"`) - - valid, err = s.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } - - var hmacVerifier HMACKey = "yyqz64U$eG?eUAp24Pm!Fn!Cn" - var s2 signature - s2.Algorithm = HS2019 - s2.KeyID = "secondary" - s2.Headers = []string{"foo"} - sig := "3RCLz6TH2I32nj1NY5YaUWDSCNPiKsAVIXjX4merDeNvrGondy7+f3sWQQJWRwEo90FCrthWrrVcgHqqFevS9Q==" - - req, reqErr := http.NewRequest("GET", "http://example.org/foo2", nil) - if reqErr != nil { - t.Error(reqErr) - } - - req.Header.Set("Foo", "bar") - req.Header.Set("Signature", `keyId="secondary",algorithm="hs2019",headers="digest",signature="`+sig+`"`) - - valid, err = s2.Verify(hmacVerifier, nil, req) - if err != nil { - t.Error("Unexpected error while building signing string:", err) - } - if !valid { - t.Error("The signature should be valid") - } - - sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - req.Header.Set("Signature", `keyId="secondary",algorithm="hs2019",headers="digest",signature="`+sig+`"`) - - valid, err = s2.Verify(hmacVerifier, nil, req) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } - - // verify with body - var s3 signature - s3.Algorithm = ED25519 - s3.KeyID = "primary" - s3.Headers = []string{"digest", "foo"} - s3.Sig = "HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA==" - body := []byte("{\"hello\": \"world\"}\n") - - r, err = http.NewRequest("GET", "http://example.org/foo", ioutil.NopCloser(bytes.NewBuffer(body))) - if err != nil { - t.Error(err) - } - - r.Header.Set("Foo", "bar") - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s3.Sig+`"`) - - valid, err = s3.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if !valid { - t.Error("The signature should be valid") - } - - r.Header.Set("Foo", "bar") - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s3.Sig+`"`) - - sp, err := SignatureParamsFromRequest(r) - if err != nil { - t.Error("Unexpected error while extracting signature parameters") - } - valid, err = sp.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if !valid { - t.Error("The signature should be valid") - } - - s3.Sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s3.Sig+`"`) - - valid, err = s3.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } - - // request with a different body should fail to validate - body = []byte("{\"world\": \"hello\"}\n") - r.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - s3.Sig = "HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA==" - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s3.Sig+`"`) - - valid, err = s3.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } -} - -func TestVerifyRequest(t *testing.T) { - var pubKey Ed25519PubKey - pubKey, err := hex.DecodeString("e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d") - if err != nil { - t.Error(err) - } - - var sp SignatureParams - sp.Algorithm = ED25519 - sp.KeyID = "primary" - sp.Headers = []string{"foo"} - - pkv := ParameterizedKeystoreVerifier{ - SignatureParams: sp, - Keystore: &StaticKeystore{pubKey}, - Opts: crypto.Hash(0), - } - - sig := "RbGSX1MttcKCpCkq9nsPGkdJGUZsAU+0TpiXJYkwde+0ZwxEp9dXO3v17DwyGLXjv385253RdGI7URbrI7J6DQ==" - - r, err := http.NewRequest("GET", "http://example.org/foo", nil) - if err != nil { - t.Error(err) - } - - r.Header.Set("Foo", "bar") - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest",signature="`+sig+`"`) - - _, keyID, err := pkv.VerifyRequest(r) - if err != nil { - t.Error("Unexpected error, signature should be valid:", err) - } - if keyID != "primary" { - t.Error("The keyID should match") - } - - sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest",signature="`+sig+`"`) - - _, keyID, err = pkv.VerifyRequest(r) - if err == nil { - t.Error("Missing expected error, signature should be invalid:", err) - } - if keyID == "primary" { - t.Error("The keyId should not match") - } - - var hmacVerifier HMACKey = "yyqz64U$eG?eUAp24Pm!Fn!Cn" - var sp2 SignatureParams - sp2.Algorithm = HS2019 - sp2.KeyID = "secondary" - sp2.Headers = []string{"foo"} - - pkv2 := ParameterizedKeystoreVerifier{ - SignatureParams: sp2, - Keystore: &StaticKeystore{hmacVerifier}, - Opts: crypto.Hash(0), - } - - sig = "3RCLz6TH2I32nj1NY5YaUWDSCNPiKsAVIXjX4merDeNvrGondy7+f3sWQQJWRwEo90FCrthWrrVcgHqqFevS9Q==" - - req, reqErr := http.NewRequest("GET", "http://example.org/foo2", nil) - if reqErr != nil { - t.Error(reqErr) - } - - req.Header.Set("Foo", "bar") - req.Header.Set("Signature", `keyId="secondary",algorithm="hs2019",headers="digest",signature="`+sig+`"`) - - _, keyID, err = pkv2.VerifyRequest(req) - if err != nil { - t.Error("Unexpected error, signature should be valid:", err) - } - if keyID != "secondary" { - t.Error("The keyId should match") - } - - sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - req.Header.Set("Signature", `keyId="secondary",algorithm="hs2019",headers="digest",signature="`+sig+`"`) - - _, keyID, err = pkv2.VerifyRequest(req) - if err == nil { - t.Error("Missing expected error, signature should be invalid:", err) - } - if keyID == "secondary" { - t.Error("The keyId should not match") - } -} - -func TestTextMarshal(t *testing.T) { - var s signature - s.Algorithm = ED25519 - s.KeyID = "Test" - s.Headers = []string{"(request-target)", "host", "date", "content-type", "digest", "content-length"} - s.Sig = "Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=" - - b, err := s.MarshalText() - if err != nil { - t.Error("Unexpected error during marshal") - } - - expected := "keyId=\"Test\",algorithm=\"ed25519\",headers=\"(request-target) host date content-type digest content-length\",signature=\"Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=\"" - - if string(b) != expected { - t.Error("Incorrect string value from marshal") - } - - s.Headers = []string{} - - b, err = s.MarshalText() - if err != nil { - t.Error("Unexpected error during marshal") - } - - expected = "keyId=\"Test\",algorithm=\"ed25519\",signature=\"Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=\"" - - if string(b) != expected { - t.Error("Incorrect string value from marshal") - } -} - -func TestTextUnmarshal(t *testing.T) { - var expected signature - expected.Algorithm = ED25519 - expected.KeyID = "Test" - expected.Headers = []string{"(request-target)", "host", "date", "content-type", "digest", "content-length"} - expected.Sig = "Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=" - - marshalled := "Signature keyId=\"Test\",algorithm=\"ed25519\",headers=\"(request-target) host date content-type digest content-length\",signature=\"Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=\"" - - var s signature - err := s.UnmarshalText([]byte(marshalled)) - if err != nil { - t.Error("Unexpected error during unmarshal") - } - - if !reflect.DeepEqual(s, expected) { - t.Error("Incorrect result from unmarshal") - } - - // Duplicated field - marshalled = "Signature keyId=\"Foo\",algorithm=\"ed25519\",headers=\"(request-target) host date content-type digest content-length\",signature=\"Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=\",keyId=\"Test\"" - - err = s.UnmarshalText([]byte(marshalled)) - if err != nil { - t.Error("Unexpected error during unmarshal") - } - - if !reflect.DeepEqual(s, expected) { - t.Error("Incorrect result from unmarshal") - } - - // Missing required field - marshalled = "Signature algorithm=\"ed25519\",headers=\"(request-target) host date content-type digest content-length\",signature=\"Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=\"" - - err = s.UnmarshalText([]byte(marshalled)) - if err == nil { - t.Error("No error with missing required field keyId") - } - - // Missing optional field - marshalled = "Signature keyId=\"Test\",algorithm=\"ed25519\",signature=\"Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=\"" - - err = s.UnmarshalText([]byte(marshalled)) - if err != nil { - t.Error("Error with missing optional field headers") - } -} - -func TestSignatureParamsFromRequest(t *testing.T) { - var privKey ed25519.PrivateKey - privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" - privKey, err := hex.DecodeString(privHex) - if err != nil { - t.Error(err) - } - - var s signature - s.Algorithm = ED25519 - s.KeyID = "primary" - s.Headers = []string{"foo"} - - r, err := http.NewRequest("GET", "http://example.org/foo", nil) - if err != nil { - t.Error(err) - } - r.Header.Set("Foo", "bar") - - err = s.Sign(privKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building ED25519 signing string:", err) - } - - sp, err := SignatureParamsFromRequest(r) - if err != nil { - t.Error("Unexpected error while retrieving signature params:", err) - } - if !reflect.DeepEqual(*sp, s.SignatureParams) { - t.Error("signature params should match!") - } - - s.Algorithm = HS2019 - if reflect.DeepEqual(*sp, s.SignatureParams) { - t.Error("signature params should not match!") - } -} - -func TestSignResponse(t *testing.T) { - var privKey ed25519.PrivateKey - privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" - privKey, err := hex.DecodeString(privHex) - if err != nil { - t.Error(err) - } - - var sp SignatureParams - sp.Algorithm = ED25519 - sp.KeyID = "primary" - sp.Headers = []string{"digest", "foo"} - body := []byte("{\"hello\": \"world\"}\n") - - ps := ParameterizedSignator{ - SignatureParams: sp, - Signator: privKey, - Opts: crypto.Hash(0), - } - - resp := &http.Response{Header: http.Header{}} - resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - resp.Header.Set("Foo", "bar") - - err = ps.SignResponse(resp) - if err != nil { - t.Error("Unexpected error while building ED25519 signing string:", err) - } - - if resp.Header.Get("Digest") != "SHA-256=RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=" { - t.Error("Incorrect digest generated for '{\"hello\", \"world\"}\\n'") - } - - var s signature - err = s.UnmarshalText([]byte(resp.Header.Get("Signature"))) - if err != nil { - t.Error(err) - } - - if s.Sig != "HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA==" { - t.Error("Incorrect signature genearted for ED25519") - } -} - -type MockResponseWriter struct { - h http.Header - b io.ReadCloser -} - -func (mrw MockResponseWriter) Header() http.Header { - return mrw.h -} - -func (mrw MockResponseWriter) WriteHeader(statusCode int) { -} - -func (mrw MockResponseWriter) Write(body []byte) (int, error) { - mrw.b = io.NopCloser(bytes.NewBuffer(body)) - return len(body), nil -} - -func TestParameterizedSignatorResponseWriter(t *testing.T) { - var privKey ed25519.PrivateKey - privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" - privKey, err := hex.DecodeString(privHex) - if err != nil { - t.Error(err) - } - - var sp SignatureParams - sp.Algorithm = ED25519 - sp.KeyID = "primary" - sp.Headers = []string{"digest", "foo", "date"} - - ps := ParameterizedSignator{ - SignatureParams: sp, - Signator: privKey, - Opts: crypto.Hash(0), - } - - mw := &MockResponseWriter{h: http.Header{}} - psw := NewParameterizedSignatorResponseWriter(ps, mw) - var w http.ResponseWriter - w = psw - - w.Header().Set("Foo", "bar") - w.WriteHeader(200) - if psw.statusCode != 200 { - t.Error("Status code did not match") - } - - body := []byte("{\"hello\": \"world\"}\n") - // First write should fail, as we have not provided the date header required above - _, err = w.Write(body) - if err == nil { - t.Error("Should have failed due to missing date header") - } - - // Set date header and succeed this time - dateHeaderValue, err := time.Parse(time.RFC1123, "Tue, 10 Nov 2009 23:00:00 UTC") - if err != nil { - t.Error(err) - } - w.Header().Set("date", dateHeaderValue.Format(http.TimeFormat)) - - _, err = w.Write(body) - if err != nil { - t.Error("Unexpected error:", err) - } - - if w.Header().Get("Digest") != "SHA-256=RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=" { - t.Error("Incorrect digest generated for '{\"hello\", \"world\"}\\n'") - } - - var s signature - err = s.UnmarshalText([]byte(w.Header().Get("Signature"))) - if err != nil { - t.Error(err) - } - - // perform verification of the response - resp := &http.Response{Header: mw.h, Body: mw.b} - valid, err := s.VerifyResponse(Ed25519PubKey(privKey.Public().(ed25519.PublicKey)), crypto.Hash(0), resp) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } -} - -//func TestVerifyResponse(t *testing.T) { -// var pubKey Ed25519PubKey -// pubKey, err := hex.DecodeString("e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d") -// if err != nil { -// t.Error(err) -// } -// -// var s signature -// s.Algorithm = ED25519 -// s.KeyID = "primary" -// s.Headers = []string{"digest", "foo"} -// s.Sig = "HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA==" -// body := []byte("{\"hello\": \"world\"}\n") -// -// resp := &http.Response{Header: http.Header{}} -// resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) -// resp.Header.Set("Foo", "bar") -// resp.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s.Sig+`"`) -// -// // Fail first verification attempt because the date header is required above but is not set -// valid, err := s.VerifyResponse(pubKey, crypto.Hash(0), resp) -// if err == nil { -// t.Error("Should have failed due to missing date header") -// } -// dateHeaderValue, err := time.Parse(time.RFC1123, "Tue, 07 Nov 2023 23:00:00 UTC") -// if err != nil { -// t.Error(err) -// } -// resp.Header.Set("date", dateHeaderValue.Format(http.TimeFormat)) -// -// // Verify again, passing this time now that the date header is set -// valid, err = s.VerifyResponse(pubKey, crypto.Hash(0), resp) -// if err != nil { -// t.Error("Unexpected error while building signing string:", err) -// } -// -// if !valid { -// t.Error("The signature should be valid") -// } -// -// s.Sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -// resp.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s.Sig+`"`) -// -// valid, err = s.VerifyResponse(pubKey, crypto.Hash(0), resp) -// if err != nil { -// t.Error("Unexpected error while building signing string") -// } -// if valid { -// t.Error("The signature should be invalid") -// } -// -// // request with a different body should fail to validate -// body = []byte("{\"world\": \"hello\"}\n") -// resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) -// s.Sig = "HvrmTu+A96H46IPZAYC2rmqRSgmgUgCcyPcnCikX0eGPSC6Va5jyr3blRLjpbGk6UMJ1FXckdWFnJxkt36gkBA==" -// resp.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest foo",signature="`+s.Sig+`"`) -// -// valid, err = s.VerifyResponse(pubKey, crypto.Hash(0), resp) -// if err != nil { -// t.Error("Unexpected error while building signing string") -// } -// if valid { -// t.Error("The signature should be invalid") -// } -//} diff --git a/libs/httpsignature/nitro.go b/libs/httpsignature/nitro.go deleted file mode 100644 index 94f8aec3d..000000000 --- a/libs/httpsignature/nitro.go +++ /dev/null @@ -1,16 +0,0 @@ -package httpsignature - -import ( - "github.com/brave-intl/bat-go/libs/nitro" -) - -// NitroSigner is a placeholder struct to sign using a nitro attestation -type NitroSigner struct{ nitro.Signer } - -// NitroVerifier specifies the PCR values required for verification -type NitroVerifier struct{ nitro.Verifier } - -// NewNitroVerifier returns a new verifier for nitro attestations -func NewNitroVerifier(pcrs map[uint][]byte) NitroVerifier { - return NitroVerifier{nitro.NewVerifier(pcrs)} -} diff --git a/libs/httpsignature/nitro_test.go b/libs/httpsignature/nitro_test.go deleted file mode 100644 index 0992954d2..000000000 --- a/libs/httpsignature/nitro_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package httpsignature - -import ( - "crypto" - "encoding/hex" - "os" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/nitro" -) - -func now() time.Time { - return time.Date(2023, time.March, 28, 12, 0, 0, 0, time.UTC) -} - -func TestVerifyNitroAttestation(t *testing.T) { - pcr3 := "e48b6ac6bab30e3717d28c2c88f2ba8b614e454590eb00b26170eef0d707b5b8e3a97662c20b2ced6192d3aaa2f5e24e" - pcr3Decoded, err := hex.DecodeString(pcr3) - if err != nil { - t.Fatal("error decoding PCR 3:", err) - } - pcrs := map[uint][]byte{ - 3: pcr3Decoded, - } - - // FIXME we need a better attestation doc sample - // this one is over a nil userData - // https://github.com/aws-samples/aws-iot-validate-enclave-attestation - doc, err := os.ReadFile("att_doc_sample.bin") - if err != nil { - t.Fatal("error reading sample attestation doc:", err) - } - - verifier := NitroVerifier{ - nitro.Verifier{ - PCRs: pcrs, - Now: now, - }, - } - - valid, err := verifier.Verify([]byte{}, doc, crypto.Hash(0)) - if err != nil { - t.Fatal("error verifying sample attestation doc:", err) - } - if !valid { - t.Fatal("sample attestation doc was invalid") - } - - valid, err = verifier.Verify([]byte{0}, doc, crypto.Hash(0)) - if err != nil { - t.Fatal("error verifying sample attestation doc:", err) - } - if valid { - t.Fatal("sample attestation doc should be invalid for different message") - } -} diff --git a/libs/inputs/decode.go b/libs/inputs/decode.go deleted file mode 100644 index 56173af44..000000000 --- a/libs/inputs/decode.go +++ /dev/null @@ -1,23 +0,0 @@ -package inputs - -import ( - "bytes" - "context" - "encoding/json" -) - -// Decodable - and interface that allows for validation of inputs and params -type Decodable interface { - Decode(context.Context, []byte) error -} - -// Decode - decode a decodable thing -func Decode(ctx context.Context, d Decodable, input []byte) error { - return d.Decode(ctx, input) -} - -// DecodeJSON - decode json helper -func DecodeJSON(ctx context.Context, input []byte, v interface{}) error { - dec := json.NewDecoder(bytes.NewBuffer(input)) - return dec.Decode(v) -} diff --git a/libs/inputs/ids.go b/libs/inputs/ids.go deleted file mode 100644 index 6c0561def..000000000 --- a/libs/inputs/ids.go +++ /dev/null @@ -1,54 +0,0 @@ -package inputs - -import ( - "context" - "errors" - - uuid "github.com/satori/go.uuid" -) - -var ( - // ErrIDDecodeNotUUID - an error that tells caller the id is not a uuid - ErrIDDecodeNotUUID = errors.New("failed to decode id: id is not a uuid") - // ErrIDDecodeEmpty - an error that tells caller the id is empty and should not be - ErrIDDecodeEmpty = errors.New("failed to decode id: id cannot be empty") -) - -// ID - a generic ID type that can be used for common id based things -type ID struct { - uuid *uuid.UUID - raw string -} - -// UUID - return the UUID representation of the ID -func (id *ID) UUID() *uuid.UUID { - return id.uuid -} - -// String - return the String representation of the ID -func (id *ID) String() string { - return id.raw -} - -// Validate - take raw []byte input and populate id with the ID -func (id *ID) Validate(ctx context.Context) error { - // this should be overloaded to validate ids are real... - return nil -} - -// Decode - take raw []byte input and populate id with the ID -func (id *ID) Decode(ctx context.Context, input []byte) error { - var err error - - if len(input) == 0 { - return ErrIDDecodeEmpty - } - id.raw = string(input) - - var parsed uuid.UUID - if parsed, err = uuid.FromString(id.raw); err != nil { - return ErrIDDecodeNotUUID - } - id.uuid = &parsed - return nil -} diff --git a/libs/inputs/inputs.go b/libs/inputs/inputs.go deleted file mode 100644 index e351a25ad..000000000 --- a/libs/inputs/inputs.go +++ /dev/null @@ -1,45 +0,0 @@ -package inputs - -import ( - "context" - "fmt" - "io" - "io/ioutil" - - errorutils "github.com/brave-intl/bat-go/libs/errors" -) - -// DecodeValidate - decode and validate for inputs -type DecodeValidate interface { - Validatable - Decodable -} - -// DecodeAndValidateString - perform decode and validate of input in one swipe of a string input -func DecodeAndValidateString(ctx context.Context, v DecodeValidate, input string) error { - return DecodeAndValidate(ctx, v, []byte(input)) -} - -// DecodeAndValidateReader - perform decode and validate of input in one swipe -func DecodeAndValidateReader(ctx context.Context, v DecodeValidate, input io.Reader) error { - b, err := ioutil.ReadAll(input) - if err != nil { - return fmt.Errorf("failed to read input: %w", err) - } - return DecodeAndValidate(ctx, v, b) -} - -// DecodeAndValidate - perform decode and validate of input in one swipe -func DecodeAndValidate(ctx context.Context, v DecodeValidate, input []byte) error { - var me = new(errorutils.MultiError) - if err := v.Decode(ctx, input); err != nil { - me.Append(fmt.Errorf("failed decoding: %w", err)) - } - if err := v.Validate(ctx); err != nil { - me.Append(fmt.Errorf("failed validation: %w", err)) - } - if me.Count() > 0 { - return me - } - return nil -} diff --git a/libs/inputs/merchant.go b/libs/inputs/merchant.go deleted file mode 100644 index 4d1d6ea92..000000000 --- a/libs/inputs/merchant.go +++ /dev/null @@ -1,54 +0,0 @@ -package inputs - -import ( - "context" - "fmt" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/handlers" - uuid "github.com/satori/go.uuid" -) - -// MerchantID - identifier of a merchant -type MerchantID struct { - id uuid.UUID -} - -// UUID - get the UUID representation of the merchant id -func (m MerchantID) UUID() uuid.UUID { - return m.id -} - -// NewMerchantID - create a new merchant id -func NewMerchantID(ctx context.Context, v string) (*MerchantID, error) { - var merchantID = new(MerchantID) - if err := DecodeAndValidate(ctx, merchantID, []byte(v)); err != nil { - return nil, handlers.ValidationError( - "Error decoding or validating request merchant id url parameter", - map[string]interface{}{ - "merchantID": "merchantID must be a uuidv4", - }, - ) - } - return merchantID, nil -} - -// Validate - implementation of validatable interface -func (m *MerchantID) Validate(ctx context.Context) error { - // check that this merchant id is real/exists? - return nil -} - -// Decode - implementation of decodable interface -func (m *MerchantID) Decode(ctx context.Context, v []byte) error { - if len(v) == 0 || !govalidator.IsUUIDv4(string(v)) { - return fmt.Errorf("merchant id is not a uuid: %x", v) - } - - u, err := uuid.FromString(string(v)) - if err != nil { - return fmt.Errorf("unable to parse merchant id: %x", v) - } - m.id = u - return nil -} diff --git a/libs/inputs/pagination.go b/libs/inputs/pagination.go deleted file mode 100644 index 9c25658e4..000000000 --- a/libs/inputs/pagination.go +++ /dev/null @@ -1,199 +0,0 @@ -package inputs - -import ( - "context" - "errors" - "fmt" - "net/url" - "reflect" - "regexp" - "strconv" - "strings" - - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" -) - -// OrderDirection - the directionality type -type OrderDirection string - -const ( - // Ascending - ASC - Ascending OrderDirection = "ASC" - // Descending - DESC - Descending = "DESC" -) - -// PageOrder - this directionality and attribute used for ordering -type PageOrder struct { - Direction OrderDirection - Attribute string -} - -// Pagination - parameters common to pagination -// page=1&items=50&order=id -type Pagination struct { - Order []PageOrder - RawOrder []string - Page int - Items int -} - -// GetOrderBy - create the order by expression and parameters for pagination -func (p Pagination) GetOrderBy(ctx context.Context) string { - var ( - statement string - ) - // get allowed values for order from context, if nothing allow all values - if okOrder, ok := ctx.Value(appctx.PaginationOrderOptionsCTXKey).(map[string]string); ok { - for _, po := range p.Order { - if statement != "" { - // not the first statement - statement += ", " - } - statement += fmt.Sprintf("%s ", okOrder[po.Attribute]) - - if po.Direction != "" { - if po.Direction == Ascending { - statement += " ASC " - } else if po.Direction == Descending { - statement += " DESC " - } - } - } - } - return statement -} - -// Validate - implementation of validatable interface -func (p *Pagination) Validate(ctx context.Context) error { - var errs = new(errorutils.MultiError) - if p.Page < 0 { - errs.Append(errors.New("page value must be greater than or equal to 0")) - } - if p.Items <= 0 { - errs.Append(errors.New("items value must be greater than 0")) - } - - // get allowed values for order from context, if nothing allow all values - if okOrder, ok := ctx.Value(appctx.PaginationOrderOptionsCTXKey).(map[string]string); ok { - for _, o := range p.Order { - if _, ok := okOrder[o.Attribute]; !ok { - errs.Append(fmt.Errorf("order parameter '%s' is not allowed", o.Attribute)) - } - } - } - - if errs.Count() > 0 { - return errs - } - - return nil -} - -// Decode - implementation of decodable interface -func (p *Pagination) Decode(ctx context.Context, v []byte) error { - u, err := url.Parse(string(v)) - if err != nil { - return fmt.Errorf("failed to parse pagination parameters: %w", err) - } - - // get the query string parameters - q := u.Query() - - if q.Get("page") == "" { - p.Page = 0 - } else { - p.Page, err = strconv.Atoi(q.Get("page")) - if err != nil { - return fmt.Errorf("failed to parse pagination page parameter: %w", err) - } - } - - if q.Get("items") == "" { - p.Items = 10 - } else { - p.Items, err = strconv.Atoi(q.Get("items")) - if err != nil { - return fmt.Errorf("failed to parse pagination items parameter: %w", err) - } - } - - for _, v := range q["order"] { - parts := strings.Split(v, ".") - po := PageOrder{} - if parts[0] != "" { - po.Attribute = parts[0] - } - if len(parts) > 1 && parts[1] != "" { - if string(Ascending) == strings.ToUpper(parts[1]) { - po.Direction = Ascending - } else if string(Descending) == strings.ToUpper(parts[1]) { - po.Direction = Descending - } else { - return fmt.Errorf("failed to parse order direction: %s", strings.ToUpper(parts[1])) - } - } - p.Order = append(p.Order, po) - } - p.RawOrder = q["order"] - - return nil -} - -var ( - jsonTagRE = regexp.MustCompile(`json:"(.*?)"`) - dbTagRE = regexp.MustCompile(`db:"(.*?)"`) -) - -// NewPagination - create a new Pagination struct and populate from url and order options -func NewPagination(ctx context.Context, url string, v interface{}) (context.Context, *Pagination, error) { - var ( - pagination = new(Pagination) - order = map[string]string{} - ) - - // for the number of fields the struct v has - for i := 0; i < reflect.TypeOf(v).Elem().NumField(); i++ { - // get the struct tags to produce a mapping of json -> db - tag := string(reflect.TypeOf(v).Elem().Field(i).Tag) - - // if we do have a tag - if tag != "" { - var ( - k string - v string - ) - - // find the json tag - jsonMatch := jsonTagRE.FindStringSubmatch(tag) - if len(jsonMatch) > 1 { - k = strings.Split(jsonMatch[1], ",")[0] - } - dbMatch := dbTagRE.FindStringSubmatch(tag) - if len(dbMatch) > 1 { - v = strings.Split(dbMatch[1], ",")[0] - } - - if k != "" && v != "" { - order[k] = v - } - } - } - - ctx = context.WithValue(ctx, appctx.PaginationOrderOptionsCTXKey, order) - - if err := DecodeAndValidate(ctx, pagination, []byte(url)); err != nil { - var ( - veParam = map[string]interface{}{} - message = err.Error() - me *errorutils.MultiError - ) - if errors.As(err, &me) { - veParam["pagination"] = me.Errs - } - return ctx, nil, handlers.ValidationError(message, veParam) - } - return ctx, pagination, nil -} diff --git a/libs/inputs/pagination_test.go b/libs/inputs/pagination_test.go deleted file mode 100644 index 6c99d5b0f..000000000 --- a/libs/inputs/pagination_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package inputs - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -type transaction struct { - ID uuid.UUID `json:"id" db:"id"` - OrderID uuid.UUID `json:"orderId" db:"order_id"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` - ExternalTransactionID string `json:"external_transaction_id" db:"external_transaction_id"` - Status string `json:"status" db:"status"` - Currency string `json:"currency" db:"currency"` - Kind string `json:"kind" db:"kind"` - Amount decimal.Decimal `json:"amount" db:"amount"` -} - -func TestNewPagination(t *testing.T) { - var ctx = context.Background() - ctx, p, err := NewPagination(ctx, "?order=createdAt.asc&order=orderId.desc", new(transaction)) - if err != nil { - t.Error("failed to create a new pagination: ", err) - return - } - var orderBy = p.GetOrderBy(ctx) - if !strings.Contains(orderBy, "created_at ASC") || - !strings.Contains(orderBy, "order_id DESC") { - t.Logf("P: %+v\n", p.GetOrderBy(ctx)) - t.Error("order by statement not what was expected") - return - } - // test some invalid pagination - _, _, err = NewPagination(context.Background(), "?order=created_at.asc&order=orderId.BLAH", new(transaction)) - if err == nil { - t.Error("new pagination should have failed", err) - return - } - fmt.Println(err.Error()) - if !strings.Contains(err.Error(), "created_at") || !strings.Contains(err.Error(), "BLAH") { - t.Error("new pagination should have complained about created_at and BLAH") - return - } -} diff --git a/libs/inputs/publickey.go b/libs/inputs/publickey.go deleted file mode 100644 index 3c5631110..000000000 --- a/libs/inputs/publickey.go +++ /dev/null @@ -1,39 +0,0 @@ -package inputs - -import ( - "context" - "encoding/hex" - "errors" - "fmt" -) - -var ( - // ErrPublicKeyDecodeEmpty - an error that tells caller the public key is empty and should not be - ErrPublicKeyDecodeEmpty = errors.New("failed to decode public key: public key cannot be empty") -) - -// PublicKey - a generic ID type that can be used for common id based things -type PublicKey string - -// String - return the String representation of the public key -func (pk *PublicKey) String() string { - return string(*pk) -} - -// Validate - take raw []byte input and populate public key with the value -func (pk *PublicKey) Validate(ctx context.Context) error { - _, err := hex.DecodeString(string(*pk)) - if err != nil { - return fmt.Errorf("invalid public key, not hex encoded: %w", err) - } - return nil -} - -// Decode - take raw []byte input and populate id with the public key -func (pk *PublicKey) Decode(ctx context.Context, input []byte) error { - if len(input) == 0 { - return ErrPublicKeyDecodeEmpty - } - *pk = PublicKey(string(input)) - return nil -} diff --git a/libs/inputs/validate.go b/libs/inputs/validate.go deleted file mode 100644 index 3d6370af5..000000000 --- a/libs/inputs/validate.go +++ /dev/null @@ -1,13 +0,0 @@ -package inputs - -import "context" - -// Validatable - and interface that allows for validation of inputs and params -type Validatable interface { - Validate(context.Context) error -} - -// Validate - a validatable thing -func Validate(ctx context.Context, v Validatable) error { - return v.Validate(ctx) -} diff --git a/libs/jsonutils/jsonstringarray.go b/libs/jsonutils/jsonstringarray.go deleted file mode 100644 index 96ed4d490..000000000 --- a/libs/jsonutils/jsonstringarray.go +++ /dev/null @@ -1,56 +0,0 @@ -package jsonutils - -import ( - "database/sql/driver" - "encoding/json" - - "github.com/jmoiron/sqlx/types" -) - -// JSONStringArray is a wrapper around a string array for sql serialization purposes -type JSONStringArray []string - -// Scan the src sql type into the passed JSONStringArray -func (arr *JSONStringArray) Scan(src interface{}) error { - var jt types.JSONText - - if err := jt.Scan(src); err != nil { - return err - } - - if err := jt.Unmarshal(arr); err != nil { - return err - } - - return nil -} - -// Value the driver.Value representation -func (arr *JSONStringArray) Value() (driver.Value, error) { - var jt types.JSONText - - data, err := json.Marshal((*[]string)(arr)) - if err != nil { - return nil, err - } - - if err := jt.UnmarshalJSON(data); err != nil { - return nil, err - } - - return jt.Value() -} - -// MarshalJSON returns the JSON representation -func (arr *JSONStringArray) MarshalJSON() ([]byte, error) { - return json.Marshal((*[]string)(arr)) -} - -// UnmarshalJSON sets the passed JSONStringArray to the value deserialized from JSON -func (arr *JSONStringArray) UnmarshalJSON(data []byte) error { - if err := json.Unmarshal(data, (*[]string)(arr)); err != nil { - return err - } - - return nil -} diff --git a/libs/kafka/consumer.go b/libs/kafka/consumer.go deleted file mode 100644 index 313223724..000000000 --- a/libs/kafka/consumer.go +++ /dev/null @@ -1,66 +0,0 @@ -package kafka - -import ( - "context" - "fmt" - - "github.com/brave-intl/bat-go/libs/logging" - "github.com/getsentry/sentry-go" - kafkago "github.com/segmentio/kafka-go" -) - -// Handler defines a handler. -type Handler interface { - Handle(ctx context.Context, message kafkago.Message) error -} - -// ErrorHandler defines an error handler. -type ErrorHandler interface { - Handle(ctx context.Context, message kafkago.Message, errorMessage error) error -} - -// TODO: Commit many messages off cache. - -// Consume implements consumer loop. -func Consume(ctx context.Context, reader Consumer, handler Handler, errorHandler ErrorHandler) error { - logger := logging.Logger(ctx, "kafka consumer") - logger.Info().Msg("starting consumer") - - for { - select { - case <-ctx.Done(): - return ctx.Err() - default: - message, err := reader.FetchMessage(ctx) - if err != nil { - return fmt.Errorf("error fetching message key %s partition %d offset %d: %w", - string(message.Key), message.Partition, message.Offset, err) - } - - err = handler.Handle(ctx, message) - if err != nil { - sentry.CaptureException(err) - logger.Err(err).Msg("error processing message sending to dlq") - err := errorHandler.Handle(ctx, message, err) - if err != nil { - logger.Err(err). - Str("key", string(message.Key)). - Int("partition", message.Partition). - Int64("offset", message.Offset). - Msg("error writing message to dlq") - return fmt.Errorf("error writing message to dlq: %w", err) - } - } - - err = reader.CommitMessages(ctx, message) - if err != nil { - logger.Err(err). - Str("key", string(message.Key)). - Int("partition", message.Partition). - Int64("offset", message.Offset). - Msg("error committing kafka message") - sentry.CaptureException(err) - } - } - } -} diff --git a/libs/kafka/dialer.go b/libs/kafka/dialer.go deleted file mode 100644 index 7e58cc32f..000000000 --- a/libs/kafka/dialer.go +++ /dev/null @@ -1,229 +0,0 @@ -package kafka - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "os" - "strings" - "time" - - "github.com/linkedin/goavro" - "github.com/segmentio/kafka-go" - - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/logging" -) - -// Consumer defines methods for consuming kafka messages. -type Consumer interface { - ReadMessage(ctx context.Context) (kafka.Message, error) - FetchMessage(ctx context.Context) (kafka.Message, error) - CommitMessages(ctx context.Context, messages ...kafka.Message) error - Close() error -} - -// Reader is an implementation of the kafka.Consumer interface. -type Reader struct { - kafkaReader *kafka.Reader - kafkaDialer *kafka.Dialer -} - -// NewKafkaReader creates a new kafka reader for groupID and topic. -func NewKafkaReader(ctx context.Context, groupID string, topic string) (*Reader, error) { - logger := logging.Logger(ctx, "kafka.NewKafkaReader") - - dialer, x509Cert, err := TLSDialer() - if err != nil { - return nil, fmt.Errorf("kafka reader: could not create new kafka reader: %w", err) - } - - // throw the cert on the context, instrument kafka - InstrumentKafka(context.WithValue(ctx, appctx.Kafka509CertCTXKey, x509Cert)) - - kafkaBrokers := ctx.Value(appctx.KafkaBrokersCTXKey).(string) - - kafkaReader := kafka.NewReader(kafka.ReaderConfig{ - Brokers: strings.Split(kafkaBrokers, ","), - RetentionTime: 10080 * time.Minute, // 7 Days - GroupID: groupID, - Topic: topic, - Dialer: dialer, - Logger: kafka.LoggerFunc(logger.Printf), // FIXME - }) - - return &Reader{ - kafkaReader: kafkaReader, - kafkaDialer: dialer, - }, nil -} - -// ReadMessage - reads kafka messages -func (k *Reader) ReadMessage(ctx context.Context) (kafka.Message, error) { - return k.kafkaReader.ReadMessage(ctx) -} - -// FetchMessage reads and return the next message. -// FetchMessage does not commit offsets automatically when using consumer groups. -func (k *Reader) FetchMessage(ctx context.Context) (kafka.Message, error) { - return k.kafkaReader.FetchMessage(ctx) -} - -// CommitMessages commits the list of messages passed as argument. -func (k *Reader) CommitMessages(ctx context.Context, messages ...kafka.Message) error { - return k.kafkaReader.CommitMessages(ctx, messages...) -} - -func (k *Reader) Close() error { - return k.kafkaReader.Close() -} - -// TLSDialer creates a Kafka dialer over TLS. The function requires -// KAFKA_SSL_CERTIFICATE_LOCATION and KAFKA_SSL_KEY_LOCATION environment -// variables to be set. -func TLSDialer() (*kafka.Dialer, *x509.Certificate, error) { - - caPEM, err := readFileFromEnvLoc("KAFKA_SSL_CA_LOCATION", false) - if err != nil { - return nil, nil, err - } - - certEnv := "KAFKA_SSL_CERTIFICATE" - certPEM := []byte(os.Getenv(certEnv)) - if len(certPEM) == 0 { - certPEM, err = readFileFromEnvLoc("KAFKA_SSL_CERTIFICATE_LOCATION", true) - if err != nil { - return nil, nil, err - } - } - - keyEnv := "KAFKA_SSL_KEY" - encryptedKeyPEM := []byte(os.Getenv(keyEnv)) - - // Check to see if KAFKA_SSL_CERTIFICATE includes both certificate and key - if certPEM[0] == '{' { - type Certificate struct { - Certificate string `json:"certificate"` - Key string `json:"key"` - } - var cert Certificate - err := json.Unmarshal(certPEM, &cert) - if err != nil { - return nil, nil, err - } - certPEM = []byte(cert.Certificate) - encryptedKeyPEM = []byte(cert.Key) - } - - if len(encryptedKeyPEM) == 0 { - encryptedKeyPEM, err = readFileFromEnvLoc("KAFKA_SSL_KEY_LOCATION", true) - if err != nil { - return nil, nil, err - } - } - - block, rest := pem.Decode(encryptedKeyPEM) - if len(rest) > 0 { - return nil, nil, errors.New("extra data in KAFKA_SSL_KEY") - } - - keyPEM := pem.EncodeToMemory(block) - - certificate, err := tls.X509KeyPair([]byte(certPEM), keyPEM) - if err != nil { - return nil, nil, errorutils.Wrap(err, "Could not parse x509 keypair") - } - - // Define TLS configuration - config := &tls.Config{ - Certificates: []tls.Certificate{certificate}, - } - - // Instrument kafka cert expiration information - x509Cert, err := x509.ParseCertificate(certificate.Certificate[0]) - if err != nil { - return nil, nil, errorutils.Wrap(err, "Could not parse certificate") - } - - if time.Now().After(x509Cert.NotAfter) { - // the certificate has expired, raise error - return nil, nil, errorutils.ErrCertificateExpired - } - - if len(caPEM) > 0 { - caCertPool := x509.NewCertPool() - if ok := caCertPool.AppendCertsFromPEM([]byte(caPEM)); !ok { - return nil, nil, errors.New("could not add custom CA from KAFKA_SSL_CA_LOCATION") - } - config.RootCAs = caCertPool - } - - dialer := &kafka.Dialer{ - Timeout: 10 * time.Second, - DualStack: true, - TLS: config} - - return dialer, x509Cert, nil -} - -func readFileFromEnvLoc(env string, required bool) ([]byte, error) { - loc := os.Getenv(env) - if len(loc) == 0 { - if !required { - return []byte{}, nil - } - return []byte{}, errors.New(env + " must be passed") - } - buf, err := ioutil.ReadFile(loc) - if err != nil { - return []byte{}, err - } - return buf, nil -} - -// InitKafkaWriter - create a kafka writer given a topic -func InitKafkaWriter(ctx context.Context, topic string) (*kafka.Writer, *kafka.Dialer, error) { - logger := logging.Logger(ctx, "kafka.InitKafkaWriter") - - dialer, x509Cert, err := TLSDialer() - if err != nil { - return nil, nil, err - } - - // throw the cert on the context, instrument kafka - InstrumentKafka(context.WithValue(ctx, appctx.Kafka509CertCTXKey, x509Cert)) - - kafkaBrokers := ctx.Value(appctx.KafkaBrokersCTXKey).(string) - - kafkaWriter := kafka.NewWriter(kafka.WriterConfig{ - Brokers: strings.Split(kafkaBrokers, ","), - Balancer: &kafka.LeastBytes{}, - Dialer: dialer, - Topic: topic, - BatchTimeout: 1 * time.Second, - Logger: kafka.LoggerFunc(logger.Printf), // FIXME - }) - - return kafkaWriter, dialer, nil -} - -// GenerateCodecs - create a map of codec name to the avro codec -func GenerateCodecs(codecs map[string]string) (map[string]*goavro.Codec, error) { - var ( - res = make(map[string]*goavro.Codec) - err error - ) - for k, v := range codecs { - res[k], err = goavro.NewCodec(v) - if err != nil { - return nil, fmt.Errorf("failed to generate codec: %w", err) - } - } - return res, nil -} diff --git a/libs/kafka/instrument.go b/libs/kafka/instrument.go deleted file mode 100644 index f311655f3..000000000 --- a/libs/kafka/instrument.go +++ /dev/null @@ -1,57 +0,0 @@ -package kafka - -import ( - "context" - "crypto/x509" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/prometheus/client_golang/prometheus" -) - -var ( - // kafkaCertNotAfter checks when the kafka certificate becomes valid - kafkaCertNotBefore = prometheus.NewGauge( - prometheus.GaugeOpts{ - Name: "kafka_cert_not_before", - Help: "Date when the kafka certificate becomes valid.", - }, - ) - - // kafkaCertNotAfter checks when the kafka certificate expires - kafkaCertNotAfter = prometheus.NewGauge( - prometheus.GaugeOpts{ - Name: "kafka_cert_not_after", - Help: "Date when the kafka certificate expires.", - }, - ) -) - -// InstrumentKafka - setup instrumentation and metrics around our kafka connection -func InstrumentKafka(ctx context.Context) { - logger := logging.Logger(ctx, "kafka.InstrumentKafka") - - cert, ok := ctx.Value(appctx.Kafka509CertCTXKey).(*x509.Certificate) - if !ok { - // no cert on context - logger.Info().Msg("no kafka cert on context, not initializing kafka instrumentation") - return - } - - err := prometheus.Register(kafkaCertNotAfter) - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - kafkaCertNotAfter = ae.ExistingCollector.(prometheus.Gauge) - } - - err = prometheus.Register(kafkaCertNotBefore) - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - kafkaCertNotBefore = ae.ExistingCollector.(prometheus.Gauge) - } - - logger.Info().Msg("registered kafka cert not before and not after prom metrics") - - kafkaCertNotBefore.Set(float64(cert.NotBefore.Unix())) - kafkaCertNotAfter.Set(float64(cert.NotAfter.Unix())) - - logger.Info().Msg("set values for kafka cert not before and not after prom metrics") -} diff --git a/libs/kafka/mock/dialer.go b/libs/kafka/mock/dialer.go deleted file mode 100644 index 7a4331f02..000000000 --- a/libs/kafka/mock/dialer.go +++ /dev/null @@ -1,99 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./kafka/dialer.go - -// Package mockdialer is a generated GoMock package. -package mockdialer - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - kafka "github.com/segmentio/kafka-go" -) - -// MockConsumer is a mock of Consumer interface. -type MockConsumer struct { - ctrl *gomock.Controller - recorder *MockConsumerMockRecorder -} - -// MockConsumerMockRecorder is the mock recorder for MockConsumer. -type MockConsumerMockRecorder struct { - mock *MockConsumer -} - -// NewMockConsumer creates a new mock instance. -func NewMockConsumer(ctrl *gomock.Controller) *MockConsumer { - mock := &MockConsumer{ctrl: ctrl} - mock.recorder = &MockConsumerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockConsumer) EXPECT() *MockConsumerMockRecorder { - return m.recorder -} - -// Close mocks base method. -func (m *MockConsumer) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close. -func (mr *MockConsumerMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConsumer)(nil).Close)) -} - -// CommitMessages mocks base method. -func (m *MockConsumer) CommitMessages(ctx context.Context, messages ...kafka.Message) error { - m.ctrl.T.Helper() - varargs := []interface{}{ctx} - for _, a := range messages { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "CommitMessages", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// CommitMessages indicates an expected call of CommitMessages. -func (mr *MockConsumerMockRecorder) CommitMessages(ctx interface{}, messages ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx}, messages...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitMessages", reflect.TypeOf((*MockConsumer)(nil).CommitMessages), varargs...) -} - -// FetchMessage mocks base method. -func (m *MockConsumer) FetchMessage(ctx context.Context) (kafka.Message, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchMessage", ctx) - ret0, _ := ret[0].(kafka.Message) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchMessage indicates an expected call of FetchMessage. -func (mr *MockConsumerMockRecorder) FetchMessage(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMessage", reflect.TypeOf((*MockConsumer)(nil).FetchMessage), ctx) -} - -// ReadMessage mocks base method. -func (m *MockConsumer) ReadMessage(ctx context.Context) (kafka.Message, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadMessage", ctx) - ret0, _ := ret[0].(kafka.Message) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ReadMessage indicates an expected call of ReadMessage. -func (mr *MockConsumerMockRecorder) ReadMessage(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadMessage", reflect.TypeOf((*MockConsumer)(nil).ReadMessage), ctx) -} diff --git a/libs/kv/kv.go b/libs/kv/kv.go deleted file mode 100644 index dd772b841..000000000 --- a/libs/kv/kv.go +++ /dev/null @@ -1,46 +0,0 @@ -// Package kv contains "key-value" stores used for testing -package kv - -import "errors" - -// UnsafeMapKv is an unsafe key-value store implementation using a map for testing -type UnsafeMapKv struct { - m map[string]string -} - -// NewUnsafe creates a new UnsafeMapKv -func NewUnsafe() *UnsafeMapKv { - return &UnsafeMapKv{m: map[string]string{}} -} - -// Set the key to value, updating possible existing if upsert is true -// NOTE ttl is ignored for this implementation -func (m *UnsafeMapKv) Set(key string, value string, ttl int, upsert bool) (bool, error) { - if _, ok := m.m[key]; !ok || upsert { - m.m[key] = value - return true, nil - } - return false, errors.New("Set failed") -} - -// Get the value at key -func (m *UnsafeMapKv) Get(key string) (string, error) { - if value, ok := m.m[key]; ok { - return value, nil - } - return "", errors.New("Get failed") -} - -// Delete the value at key -func (m *UnsafeMapKv) Delete(key string) (bool, error) { - if _, ok := m.m[key]; ok { - delete(m.m, key) - return true, nil - } - return false, nil -} - -// Close the store (no-op) -func (m *UnsafeMapKv) Close() error { - return nil -} diff --git a/libs/kv/kv_test.go b/libs/kv/kv_test.go deleted file mode 100644 index c427b57bc..000000000 --- a/libs/kv/kv_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package kv - -import "testing" - -func TestMapKv(t *testing.T) { - store := UnsafeMapKv{m: map[string]string{}} - - if r, err := store.Set("FOO", "BAR", -1, true); !r || err != nil { - t.Error("Set to empty kv store should always succeed") - } - if r, err := store.Get("FOO"); r != "BAR" || err != nil { - t.Error("Get should return added value") - } - - if r, err := store.Set("FOO", "CAFE", -1, true); !r || err != nil { - t.Error("Set with upsert should succeed") - } - if r, err := store.Get("FOO"); r != "CAFE" || err != nil { - t.Error("Get should return last added value") - } - - if r, err := store.Set("FOO", "DEAD", -1, false); r || err == nil { - t.Error("Add without upsert should fail") - } - if r, err := store.Get("FOO"); r != "CAFE" || err != nil { - t.Error("Get should return last successfully added value") - } - - if _, err := store.Get("DEAD"); err == nil { - t.Error("Get should return an error on nonexistant key") - } - - if r, err := store.Delete("FOO"); !r || err != nil { - t.Error("Delete should return true on success") - } - if r, err := store.Delete("FOO"); r || err != nil { - t.Error("Delete should return false when the key does not exist") - } -} diff --git a/libs/logging/logging.go b/libs/logging/logging.go deleted file mode 100644 index 1c74ab386..000000000 --- a/libs/logging/logging.go +++ /dev/null @@ -1,246 +0,0 @@ -package logging - -import ( - "context" - "encoding/json" - "fmt" - "io" - "os" - "time" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog" - "github.com/rs/zerolog/diode" - uuid "github.com/satori/go.uuid" -) - -var ( - // we are not promising to get every log message in the log - // anymore, when it comes down to it, we would rather the service - // runs than fails on log writing contention. This will let us - // see how many logs we are dropping - droppedLogTotal = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "dropped_log_events_total", - Help: "A counter for the number of dropped log messages", - }, - ) - Writer io.WriteCloser -) - -func NopCloser(w io.Writer) io.WriteCloser { - return nopCloser{w} -} - -type nopCloser struct { - io.Writer -} - -func (nopCloser) Close() error { return nil } - -func init() { - prometheus.MustRegister(droppedLogTotal) -} - -// SetupLoggerWithLevel - helper to setup a logger and associate with context with a given log level -func SetupLoggerWithLevel(ctx context.Context, level zerolog.Level) (context.Context, *zerolog.Logger) { - // setup context with log level passed in - ctx = context.WithValue(ctx, appctx.LogLevelCTXKey, level) - // call SetupLogger - return SetupLogger(ctx) -} - -// SetupLogger - helper to setup a logger and associate with context -func SetupLogger(ctx context.Context) (context.Context, *zerolog.Logger) { - writer, ok := ctx.Value(appctx.LogWriterCTXKey).(io.Writer) - - env, err := appctx.GetStringFromContext(ctx, appctx.EnvironmentCTXKey) - if err != nil { - // if not in context, default to local - env = "local" - } - - // defaults to info level - level, _ := appctx.GetLogLevelFromContext(ctx, appctx.LogLevelCTXKey) - - if ok { - Writer = NopCloser(writer) - } else if env != "local" { - // this log writer uses a ring buffer and drops messages that cannot be processed - // in a timely manner - Writer = diode.NewWriter(os.Stdout, 1000, time.Duration(20*time.Millisecond), func(missed int) { - // add to our counter of lost log messages - droppedLogTotal.Add(float64(missed)) - }) - } else { - Writer = NopCloser(zerolog.ConsoleWriter{Out: os.Stdout}) - } - - // always print out timestamp - l := zerolog.New(Writer).With().Timestamp().Logger() - - var ( - debug bool - ) - - // set the log level - l = l.Level(level) - - // debug override - if debug, ok = ctx.Value(appctx.DebugLoggingCTXKey).(bool); ok && debug { - l = l.Level(zerolog.DebugLevel) - } - - return l.WithContext(ctx), &l -} - -func UpdateContext(ctx context.Context, logger zerolog.Logger) (context.Context, *zerolog.Logger) { - ctx = logger.WithContext(ctx) - return ctx, &logger -} - -// AddWalletIDToContext adds wallet id to context -func AddWalletIDToContext(ctx context.Context, walletID uuid.UUID) { - l := zerolog.Ctx(ctx) - if e := l.Debug(); e.Enabled() { - l.UpdateContext(func(c zerolog.Context) zerolog.Context { - return c.Str("walletID", walletID.String()) - }) - } -} - -// Progress - type to store the incremental progress of a task -type Progress struct { - Processed int - Total int -} - -// SubmitProgress - helper to log progress -func SubmitProgress(ctx context.Context, processed, total int) { - progChan, progOk := ctx.Value(appctx.ProgressLoggingCTXKey).(chan Progress) - if progOk { - progChan <- Progress{ - Processed: processed, - Total: total, - } - } -} - -// ReportProgress - goroutine watching for Progress updates for logging -func ReportProgress(ctx context.Context, progressDuration time.Duration) chan Progress { - // setup logger - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = SetupLogger(ctx) - } - - // we will return the progress channel so the app - // can send us progress information as it processes - progChan := make(chan Progress) - var ( - last Progress - ) - go func() { - for { - select { - case <-time.After(progressDuration): - // output most recent progress information, but only if - // some progress has been made. - if last.Processed != 0 && last.Total-last.Processed != 0 && last.Total != 0 { - logger.Info(). - Int("processed", last.Processed). - Int("pending", last.Total-last.Processed). - Int("total", last.Total). - Msg("progress update") - } - case last = <-progChan: - continue - } - } - }() - return progChan -} - -// UpholdProgress - type to store the incremental progress of an Uphold transaction set -type UpholdProgress struct { - Message string - Count int -} - -// UpholdProgressSet - the set up uphold progresses -type UpholdProgressSet struct { - Progress []UpholdProgress -} - -// UpholdSubmitProgress - helper to log progress -func UpholdSubmitProgress(ctx context.Context, progressSet UpholdProgressSet) { - progChan, progOk := ctx.Value(appctx.ProgressLoggingCTXKey).(chan UpholdProgressSet) - if progOk { - progChan <- progressSet - } -} - -// UpholdReportProgress - goroutine watching for UpholdProgress updates for logging -func UpholdReportProgress(ctx context.Context, progressDuration time.Duration) chan UpholdProgressSet { - // setup logger - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = SetupLogger(ctx) - } - - // we will return the progress channel so the app - // can send us progress information as it processes - progChan := make(chan UpholdProgressSet) - var ( - last UpholdProgressSet - ) - go func() { - for { - select { - case <-time.After(progressDuration): - // output most recent progress information, but only if - // some progress has been made. - if len(last.Progress) > 0 { - prettyProgress, err := json.MarshalIndent(last.Progress, "", " ") - if err == nil { - logger.Info().Msg(fmt.Sprintf("progress update:\n%s", prettyProgress)) - } else { - logger.Error().Err(err).Msg("failed to prettify progress for logging") - } - } - case last = <-progChan: - continue - } - } - }() - return progChan -} - -// Logger - get a logger a little easier for payments -func Logger(ctx context.Context, prefix string) *zerolog.Logger { - l, err := appctx.GetLogger(ctx) - if err != nil { - // create a new logger - _, l = SetupLogger(ctx) - } - sl := l.With().Str("module", prefix).Logger() - return &sl -} - -// FromContext - retrieves logger from context or gets a new logger if not present -func FromContext(ctx context.Context) *zerolog.Logger { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = SetupLogger(ctx) - } - return logger -} - -// LogAndError - helper to log and error -func LogAndError(logger *zerolog.Logger, msg string, err error) error { - if logger != nil { - logger.Error().Err(err).Msg(msg) - } - return err -} diff --git a/libs/logging/logging_test.go b/libs/logging/logging_test.go deleted file mode 100644 index 501fee9a8..000000000 --- a/libs/logging/logging_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package logging - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "testing" - - "github.com/rs/zerolog" - uuid "github.com/satori/go.uuid" -) - -func TestAddWalletIDToContext(t *testing.T) { - type logLine struct { - WalletID uuid.UUID `json:"walletID"` - } - - var b bytes.Buffer - output := bufio.NewWriter(&b) - - log := zerolog.New(output).With().Timestamp().Logger() - ctx := log.WithContext(context.Background()) - - walletID := uuid.NewV4() - - AddWalletIDToContext(ctx, walletID) - - l := zerolog.Ctx(ctx) - l.Debug().Msg("test") - err := output.Flush() - if err != nil { - t.Fatal(err) - } - - var line logLine - err = json.Unmarshal(b.Bytes(), &line) - if err != nil { - t.Fatal(err) - } - - if !uuid.Equal(line.WalletID, walletID) { - t.Fatal("WalletID must be included") - } -} diff --git a/libs/metrics/db_stats.go b/libs/metrics/db_stats.go deleted file mode 100644 index 3ebe430aa..000000000 --- a/libs/metrics/db_stats.go +++ /dev/null @@ -1,185 +0,0 @@ -package metrics - -// https://github.com/dlmiddlecote/sqlstats -// Copyright (c) 2019 Daniel Middlecote -// Modified here to maintain many stats collectors for many database connections - -import ( - "database/sql" - "sync" - - "github.com/prometheus/client_golang/prometheus" -) - -const ( - namespace = "go_dbstats" - subsystem = "connections" -) - -// StatsGetter is an interface that gets sql.DBStats. -// It's implemented by e.g. *sql.DB or *sqlx.DB. -type StatsGetter interface { - Stats() sql.DBStats -} - -// SG - StatsGetter Attributes -type SG struct { - DBName string - Stats StatsGetter -} - -// StatsCollector implements the prometheus.Collector interface. -type StatsCollector struct { - mu *sync.Mutex - sgs []SG - - // descriptions of exported metrics - maxOpenDesc *prometheus.Desc - openDesc *prometheus.Desc - inUseDesc *prometheus.Desc - idleDesc *prometheus.Desc - waitedForDesc *prometheus.Desc - blockedSecondsDesc *prometheus.Desc - closedMaxIdleDesc *prometheus.Desc - closedMaxLifetimeDesc *prometheus.Desc -} - -// AddStatsGetter - Add a new db name and stats getter pair which will be reported on -func (sc *StatsCollector) AddStatsGetter(dbName string, sg StatsGetter) { - sc.mu.Lock() - defer sc.mu.Unlock() - sc.sgs = append(sc.sgs, SG{ - DBName: dbName, - Stats: sg, - }) -} - -// NewStatsCollector creates a new StatsCollector. -func NewStatsCollector(dbName string, sg StatsGetter) *StatsCollector { - return &StatsCollector{ - mu: &sync.Mutex{}, - sgs: []SG{ - { - DBName: dbName, - Stats: sg, - }, - }, - maxOpenDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "max_open"), - "Maximum number of open connections to the database.", - []string{"db_name"}, - nil, - ), - openDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "open"), - "The number of established connections both in use and idle.", - []string{"db_name"}, - nil, - ), - inUseDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "in_use"), - "The number of connections currently in use.", - []string{"db_name"}, - nil, - ), - idleDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "idle"), - "The number of idle connections.", - []string{"db_name"}, - nil, - ), - waitedForDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "waited_for"), - "The total number of connections waited for.", - []string{"db_name"}, - nil, - ), - blockedSecondsDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "blocked_seconds"), - "The total time blocked waiting for a new connection.", - []string{"db_name"}, - nil, - ), - closedMaxIdleDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "closed_max_idle"), - "The total number of connections closed due to SetMaxIdleConns.", - []string{"db_name"}, - nil, - ), - closedMaxLifetimeDesc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, "closed_max_lifetime"), - "The total number of connections closed due to SetConnMaxLifetime.", - []string{"db_name"}, - nil, - ), - } -} - -// Describe implements the prometheus.Collector interface. -func (sc StatsCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- sc.maxOpenDesc - ch <- sc.openDesc - ch <- sc.inUseDesc - ch <- sc.idleDesc - ch <- sc.waitedForDesc - ch <- sc.blockedSecondsDesc - ch <- sc.closedMaxIdleDesc - ch <- sc.closedMaxLifetimeDesc -} - -// Collect implements the prometheus.Collector interface. -func (sc StatsCollector) Collect(ch chan<- prometheus.Metric) { - for _, sg := range sc.sgs { - stats := sg.Stats.Stats() - dbName := sg.DBName - - ch <- prometheus.MustNewConstMetric( - sc.maxOpenDesc, - prometheus.GaugeValue, - float64(stats.MaxOpenConnections), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.openDesc, - prometheus.GaugeValue, - float64(stats.OpenConnections), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.inUseDesc, - prometheus.GaugeValue, - float64(stats.InUse), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.idleDesc, - prometheus.GaugeValue, - float64(stats.Idle), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.waitedForDesc, - prometheus.CounterValue, - float64(stats.WaitCount), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.blockedSecondsDesc, - prometheus.CounterValue, - stats.WaitDuration.Seconds(), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.closedMaxIdleDesc, - prometheus.CounterValue, - float64(stats.MaxIdleClosed), - dbName, - ) - ch <- prometheus.MustNewConstMetric( - sc.closedMaxLifetimeDesc, - prometheus.CounterValue, - float64(stats.MaxLifetimeClosed), - dbName, - ) - } -} diff --git a/libs/metrics/db_stats_test.go b/libs/metrics/db_stats_test.go deleted file mode 100644 index b572a14f3..000000000 --- a/libs/metrics/db_stats_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package metrics - -import ( - "database/sql" - "testing" -) - -type MockStatsGetter struct { - MockStats func() sql.DBStats -} - -func (msg *MockStatsGetter) Stats() sql.DBStats { - if msg.MockStats == nil { - return sql.DBStats{} - } - return msg.MockStats() -} - -func TestStatsCollectorInit(t *testing.T) { - sc := NewStatsCollector("testdb", &MockStatsGetter{}) - sc.AddStatsGetter("testdb1", &MockStatsGetter{}) - - // make sure both stats getters are there - if len(sc.sgs) != 2 { - t.Error("Failed to add the new stats getter") - } - - var ( - foundTest1 bool - foundTest bool - ) - - for _, sg := range sc.sgs { - if sg.DBName == "testdb" { - foundTest = true - } - if sg.DBName == "testdb1" { - foundTest1 = true - } - } - if !foundTest1 || !foundTest { - t.Error("failed to find the two required db names from stats getter") - } -} diff --git a/libs/middleware/context.go b/libs/middleware/context.go deleted file mode 100644 index a9c8bf34c..000000000 --- a/libs/middleware/context.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import ( - "context" - "net/http" - - appctx "github.com/brave-intl/bat-go/libs/context" -) - -// NewServiceCtx passes a service into the context -func NewServiceCtx(service interface{}) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := context.WithValue(r.Context(), appctx.ServiceKey, service) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} diff --git a/libs/middleware/date.go b/libs/middleware/date.go deleted file mode 100644 index b98520b78..000000000 --- a/libs/middleware/date.go +++ /dev/null @@ -1,56 +0,0 @@ -package middleware - -import ( - "net/http" - "time" - - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/logging" -) - -// VerifyDateIsRecent is a middleware that verifies a request has a date -// which is after now()-validFrom and before now()+validTo -func VerifyDateIsRecent(validFrom, validTo time.Duration) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logger := logging.Logger(r.Context(), "VerifyDateIsRecent") - - // Date: Wed, 21 Oct 2015 07:28:00 GMT - dateStr := r.Header.Get("date") - date, err := time.Parse(time.RFC1123, dateStr) - if err != nil { - logger.Error().Err(err).Msg("failed to parse the date header") - ae := handlers.AppError{ - Cause: errInvalidHeader, - Message: "Invalid date header", - Code: http.StatusBadRequest, - } - ae.ServeHTTP(w, r) - return - } - - if !date.After(time.Now().Add(-1 * validFrom)) { - logger.Error().Err(err).Msg("date is too far in the past") - ae := handlers.AppError{ - Cause: errInvalidHeader, - Message: "date is too far in the past", - Code: http.StatusRequestTimeout, - } - ae.ServeHTTP(w, r) - return - } - if !date.Before(time.Now().Add(validTo)) { - logger.Error().Err(err).Msg("date is too far in the future") - ae := handlers.AppError{ - Cause: errInvalidHeader, - Message: "date is too far in the future", - Code: http.StatusTooEarly, - } - ae.ServeHTTP(w, r) - return - } - - next.ServeHTTP(w, r) - }) - } -} diff --git a/libs/middleware/date_test.go b/libs/middleware/date_test.go deleted file mode 100644 index d23fa9a28..000000000 --- a/libs/middleware/date_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package middleware - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestVerifyDateIsRecent(t *testing.T) { - fn1 := func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("done")) - } - - handler := VerifyDateIsRecent(10*time.Minute, 10*time.Minute)(http.HandlerFunc(fn1)) - - req, err := http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code, "request without required date should fail") - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - req.Header.Set("Date", "foo") - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code, "request with invalid date should fail") - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - req.Header.Set("Date", time.Now().Add(time.Minute*60).Format(time.RFC1123)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusTooEarly, rr.Code, "request with timed out date should fail") - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - req.Header.Set("Date", time.Now().Add(time.Minute*-60).Format(time.RFC1123)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusRequestTimeout, rr.Code, "request with early date should fail") - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - req.Header.Set("Date", time.Now().Format(time.RFC1123)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "request with current date should succeed") -} diff --git a/libs/middleware/http_signed.go b/libs/middleware/http_signed.go deleted file mode 100644 index 317ab462e..000000000 --- a/libs/middleware/http_signed.go +++ /dev/null @@ -1,94 +0,0 @@ -package middleware - -import ( - "context" - "crypto" - "errors" - "net/http" - - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" -) - -var ( - errMissingSignature = errors.New("missing http signature") - errInvalidSignature = errors.New("invalid http signature") - errInvalidHeader = errors.New("invalid http header") -) - -type httpSignedKeyID struct{} - -// AddKeyID - Helpful for test cases -func AddKeyID(ctx context.Context, id string) context.Context { - return context.WithValue(ctx, httpSignedKeyID{}, id) -} - -// GetKeyID retrieves the http signing keyID from the context -func GetKeyID(ctx context.Context) (string, error) { - keyID, ok := ctx.Value(httpSignedKeyID{}).(string) - if !ok { - return "", errors.New("keyID was missing from context") - } - return keyID, nil -} - -func SignResponse(p httpsignature.ParameterizedSignator) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w = httpsignature.NewParameterizedSignatorResponseWriter(p, w) - next.ServeHTTP(w, r) - }) - } -} - -// HTTPSignedOnly is a middleware that requires an HTTP request to be signed -func HTTPSignedOnly(ks httpsignature.Keystore) func(http.Handler) http.Handler { - verifier := httpsignature.ParameterizedKeystoreVerifier{ - SignatureParams: httpsignature.SignatureParams{ - Algorithm: httpsignature.ED25519, - Headers: []string{"digest", "(request-target)"}, - }, - Keystore: ks, - Opts: crypto.Hash(0), - } - - return VerifyHTTPSignedOnly(verifier) -} - -// VerifyHTTPSignedOnly is a middleware that requires an HTTP request to be signed -// which takes a parameterized http signature verifier -func VerifyHTTPSignedOnly(verifier httpsignature.ParameterizedKeystoreVerifier) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logger := logging.Logger(r.Context(), "VerifyHTTPSignedOnly") - - if len(r.Header.Get("Signature")) == 0 { - logger.Warn().Msg("signature must be present for signed middleware") - ae := handlers.AppError{ - Cause: errMissingSignature, - Message: "signature must be present for signed middleware", - Code: http.StatusUnauthorized, - } - ae.ServeHTTP(w, r) - return - } - - ctx, keyID, err := verifier.VerifyRequest(r) - - if err != nil { - logger.Error().Err(err).Msg("failed to verify request") - ae := handlers.AppError{ - Cause: errInvalidSignature, - Message: "request signature verification failure", - Code: http.StatusForbidden, - } - ae.ServeHTTP(w, r) - return - } - - ctx = context.WithValue(ctx, httpSignedKeyID{}, keyID) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} diff --git a/libs/middleware/http_signed_test.go b/libs/middleware/http_signed_test.go deleted file mode 100644 index bf2a59217..000000000 --- a/libs/middleware/http_signed_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package middleware - -import ( - "bytes" - "context" - "crypto" - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/stretchr/testify/assert" -) - -type mockKeystore struct { - httpsignature.Verifier -} - -func (m *mockKeystore) LookupVerifier(ctx context.Context, keyID string) (context.Context, *httpsignature.Verifier, error) { - if keyID == "primary" { - return ctx, &m.Verifier, nil - } - return nil, nil, nil -} - -func TestHTTPSignedOnly(t *testing.T) { - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - assert.NoError(t, err) - _, wrongKey, err := httpsignature.GenerateEd25519Key(nil) - assert.NoError(t, err) - - keystore := mockKeystore{publicKey} - - fn1 := func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Should not have gotten here") - } - handler := HTTPSignedOnly(&keystore)(http.HandlerFunc(fn1)) - - req, err := http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusUnauthorized, rr.Code, "request without signature should fail") - - // parse the json response, make sure message matches expected - var v map[string]interface{} - err = json.NewDecoder(rr.Body).Decode(&v) - assert.NoError(t, err) - - assert.Equal(t, v["message"].(string), "signature must be present for signed middleware", "response does not match") - - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = "primary" - s.Headers = []string{"digest", "(request-target)"} - - s.KeyID = "secondary" - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) - assert.NoError(t, err) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusForbidden, rr.Code, "request with signature from wrong keyID should fail") - - s.KeyID = "primary" - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - err = s.Sign(wrongKey, crypto.Hash(0), req) - assert.NoError(t, err) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusForbidden, rr.Code, "request with signature from wrong key should fail") - - s.Headers = []string{"digest"} - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) - assert.NoError(t, err) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusForbidden, rr.Code, "request with signature from right key but wrong headers should fail") - - s.Headers = []string{"digest", "(request-target)"} - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) - assert.NoError(t, err) - req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("hello world"))) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusForbidden, rr.Code, "request with signature from right key but wrong digest should fail") - - fn2 := func(w http.ResponseWriter, r *http.Request) { - ctxKeyID, err := GetKeyID(r.Context()) - assert.NoError(t, err, "Should be able to get key id") - assert.Equal(t, "primary", ctxKeyID, "keyID should match") - } - handler = HTTPSignedOnly(&keystore)(http.HandlerFunc(fn2)) - - req, err = http.NewRequest("GET", "/hello-world", nil) - // Host is not in the header, but on the request itself - req.Host = "localhost" - assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) - assert.NoError(t, err) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "request with signature from right key should succeed") - - verifier := httpsignature.ParameterizedKeystoreVerifier{ - SignatureParams: httpsignature.SignatureParams{ - Algorithm: httpsignature.ED25519, - Headers: []string{"digest", "(request-target)", "date", "host"}, // make sure host is in signing string - }, - Keystore: &keystore, - Opts: crypto.Hash(0), - } - - handler = VerifyHTTPSignedOnly(verifier)(http.HandlerFunc(fn2)) - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) - assert.NoError(t, err) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusForbidden, rr.Code, "request without required date should fail") - - s.Headers = []string{"digest", "(request-target)", "date", "host"} - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - req.Header.Set("Date", time.Now().Format(time.RFC1123)) - err = s.Sign(privKey, crypto.Hash(0), req) - assert.NoError(t, err) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "request with current date should succeed") -} diff --git a/libs/middleware/prometheus.go b/libs/middleware/prometheus.go deleted file mode 100644 index 38da29a85..000000000 --- a/libs/middleware/prometheus.go +++ /dev/null @@ -1,211 +0,0 @@ -package middleware - -import ( - "errors" - "net/http" - - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" -) - -var ( - latencyBuckets = []float64{.25, .5, 1, 2.5, 5, 10} - - inFlightGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "in_flight_requests", - Help: "A gauge of requests currently being served by the wrapped handler.", - }) - - // ConcurrentGoRoutines holds the number of go outines - ConcurrentGoRoutines = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "concurrent_goroutine", - Help: "Gauge that holds the current number of goroutines", - }, - []string{ - "method", - }, - ) -) - -func init() { - prometheus.MustRegister(inFlightGauge, ConcurrentGoRoutines) -} - -func must(v interface{}, err error) interface{} { - if err != nil { - panic(err.Error()) - } - return v -} - -func registerIgnoreExisting(c prometheus.Collector) (interface{}, error) { - if err := prometheus.Register(c); err != nil { - var are *prometheus.AlreadyRegisteredError - if errors.As(err, &are) { - // already registered. - switch (c).(type) { - case *prometheus.CounterVec: - return are.ExistingCollector.(*prometheus.CounterVec), nil - case *prometheus.HistogramVec: - return are.ExistingCollector.(*prometheus.HistogramVec), nil - case prometheus.Gauge: - return are.ExistingCollector.(prometheus.Gauge), nil - default: - return nil, errors.New("unknown type") - } - } - } - return c, nil -} - -// InstrumentRoundTripper instruments an http.RoundTripper to capture metrics like the number -// of active requests, the total number of requests made and latency information -func InstrumentRoundTripper(roundTripper http.RoundTripper, service string) http.RoundTripper { - inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "client_in_flight_requests", - Help: "A gauge of in-flight requests for the wrapped client.", - ConstLabels: prometheus.Labels{"service": service}, - }) - // attempt to register, if already registered use the registered one - inFlightGauge = must(registerIgnoreExisting(inFlightGauge)).(prometheus.Gauge) - - counter := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "client_api_requests_total", - Help: "A counter for requests from the wrapped client.", - ConstLabels: prometheus.Labels{"service": service}, - }, - []string{"code", "method"}, - ) - // attempt to register, if already registered use the registered one - // attempt to register, if already registered use the registered one - counter = must(registerIgnoreExisting(counter)).(*prometheus.CounterVec) - - // dnsLatencyVec uses custom buckets based on expected dns durations. - // It has an instance label "event", which is set in the - // DNSStart and DNSDonehook functions defined in the - // InstrumentTrace struct below. - dnsLatencyVec := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "client_dns_duration_seconds", - Help: "Trace dns latency histogram.", - Buckets: []float64{.005, .01, .025, .05}, - ConstLabels: prometheus.Labels{"service": service}, - }, - []string{"event"}, - ) - // attempt to register, if already registered use the registered one - dnsLatencyVec = must(registerIgnoreExisting(dnsLatencyVec)).(*prometheus.HistogramVec) - - // tlsLatencyVec uses custom buckets based on expected tls durations. - // It has an instance label "event", which is set in the - // TLSHandshakeStart and TLSHandshakeDone hook functions defined in the - // InstrumentTrace struct below. - tlsLatencyVec := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "client_tls_duration_seconds", - Help: "Trace tls latency histogram.", - Buckets: []float64{.05, .1, .25, .5}, - ConstLabels: prometheus.Labels{"service": service}, - }, - []string{"event"}, - ) - // attempt to register, if already registered use the registered one - tlsLatencyVec = must(registerIgnoreExisting(tlsLatencyVec)).(*prometheus.HistogramVec) - - // histVec has no labels, making it a zero-dimensional ObserverVec. - histVec := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "client_request_duration_seconds", - Help: "A histogram of request latencies.", - Buckets: prometheus.DefBuckets, - ConstLabels: prometheus.Labels{"service": service}, - }, - []string{}, - ) - // attempt to register, if already registered use the registered one - histVec = must(registerIgnoreExisting(histVec)).(*prometheus.HistogramVec) - - // Define functions for the available httptrace.ClientTrace hook - // functions that we want to instrument. - trace := &promhttp.InstrumentTrace{ - DNSStart: func(t float64) { - dnsLatencyVec.WithLabelValues("dns_start") - }, - DNSDone: func(t float64) { - dnsLatencyVec.WithLabelValues("dns_done") - }, - TLSHandshakeStart: func(t float64) { - tlsLatencyVec.WithLabelValues("tls_handshake_start") - }, - TLSHandshakeDone: func(t float64) { - tlsLatencyVec.WithLabelValues("tls_handshake_done") - }, - } - - // Wrap the specified RoundTripper with middleware. - return promhttp.InstrumentRoundTripperInFlight(inFlightGauge, - promhttp.InstrumentRoundTripperCounter(counter, - promhttp.InstrumentRoundTripperTrace(trace, - promhttp.InstrumentRoundTripperDuration(histVec, roundTripper), - ), - ), - ) -} - -// InstrumentHandlerFunc - helper to wrap up a handler func -func InstrumentHandlerFunc(name string, f handlers.AppHandler) http.HandlerFunc { - return InstrumentHandler(name, f).ServeHTTP -} - -// InstrumentHandlerDef - definition of an instrument handler http.Handler -type InstrumentHandlerDef func(name string, h http.Handler) http.Handler - -// InstrumentHandler instruments an http.Handler to capture metrics like the number -// the total number of requests served and latency information -func InstrumentHandler(name string, h http.Handler) http.Handler { - hRequests := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "api_requests_total", - Help: "Number of requests per handler.", - ConstLabels: prometheus.Labels{"handler": name}, - }, - []string{"code", "method"}, - ) - - if err := prometheus.Register(hRequests); err != nil { - if aerr, ok := err.(prometheus.AlreadyRegisteredError); ok { - hRequests = aerr.ExistingCollector.(*prometheus.CounterVec) - } else { - panic(err) - } - } - - hLatency := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "request_duration_seconds", - Help: "A histogram of latencies for requests.", - Buckets: latencyBuckets, - ConstLabels: prometheus.Labels{"handler": name}, - }, - []string{"method"}, - ) - if err := prometheus.Register(hLatency); err != nil { - if aerr, ok := err.(prometheus.AlreadyRegisteredError); ok { - hLatency = aerr.ExistingCollector.(*prometheus.HistogramVec) - } else { - panic(err) - } - } - - return promhttp.InstrumentHandlerInFlight(inFlightGauge, - promhttp.InstrumentHandlerCounter(hRequests, promhttp.InstrumentHandlerDuration(hLatency, h)), - ) -} - -// Metrics returns a http.HandlerFunc for the prometheus /metrics endpoint -func Metrics() http.HandlerFunc { - return promhttp.Handler().(http.HandlerFunc) -} diff --git a/libs/middleware/rate_limiter.go b/libs/middleware/rate_limiter.go deleted file mode 100644 index 73cfccc5b..000000000 --- a/libs/middleware/rate_limiter.go +++ /dev/null @@ -1,103 +0,0 @@ -package middleware - -import ( - "context" - "net/http" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/gomodule/redigo/redis" - "github.com/throttled/throttled" - "github.com/throttled/throttled/store/memstore" - "github.com/throttled/throttled/store/redigostore" -) - -// IPRateLimiterWithStore rate limits based on IP using -// a provided store and a GCRA leaky bucket algorithm. -// This can be a simple memory store, a Redis store, or other stores for -// multi-instance synchronization. See -// https://github.com/throttled/throttled/tree/master/store for details. -func IPRateLimiterWithStore( - ctx context.Context, - perMin int, - burst int, - store throttled.GCRAStore, -) func(next http.Handler) http.Handler { - logger := logging.Logger(ctx, "middleware.IPRateLimiterWithStore") - - return func(next http.Handler) http.Handler { - quota := throttled.RateQuota{ - MaxRate: throttled.PerMin(perMin), - MaxBurst: burst, - } - rateLimiter, err := throttled.NewGCRARateLimiter(store, quota) - if err != nil { - logger.Fatal().Err(err) - } - - httpRateLimiter := throttled.HTTPRateLimiter{ - RateLimiter: rateLimiter, - VaryBy: &throttled.VaryBy{ - RemoteAddr: true, - Path: true, - Method: true, - }, - } - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // override for OPTIONS request methods, as sometimes many cors requests happen quickly?? - if r.Method == http.MethodOptions { - next.ServeHTTP(w, r) - } - - if !isSimpleTokenInContext(r.Context()) { - httpRateLimiter.RateLimit(next).ServeHTTP(w, r) - } else { - // override rate limiting for authorized endpoints - next.ServeHTTP(w, r) - } - }) - } -} - -// RateLimiter rate limits the number of requests a -// user from a single IP address can make using a simple -// in-memory store that will not synchronize across instances. -func RateLimiter(ctx context.Context, perMin int) func(next http.Handler) http.Handler { - logger := logging.Logger(ctx, "middleware.RateLimiter") - store, err := memstore.New(65536) - if err != nil { - logger.Fatal().Err(err) - } - // Including burst in the existing function would break the contract so it must - // be 0 until a point release. - defaultBurst := 0 - - if burst, ok := ctx.Value(appctx.RateLimiterBurstCTXKey).(int); ok { - defaultBurst = burst - } - - return IPRateLimiterWithStore(ctx, perMin, defaultBurst, store) -} - -// RateLimiterRedisStore rate limits the number of requests a -// user from a single IP address can make and coordinates request counts -// between instances using Redis. -func RateLimiterRedisStore( - ctx context.Context, - perMin int, - burst int, - redis *redis.Pool, - keyPrefix string, - db int, -) func(next http.Handler) http.Handler { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - store, err := redigostore.New(redis, keyPrefix, db) - if err != nil { - logger.Fatal().Err(err) - } - - return IPRateLimiterWithStore(ctx, perMin, burst, store) -} diff --git a/libs/middleware/rate_limiter_test.go b/libs/middleware/rate_limiter_test.go deleted file mode 100644 index 72d0a5ec0..000000000 --- a/libs/middleware/rate_limiter_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package middleware - -import ( - "context" - "github.com/alicebob/miniredis/v2" - "github.com/gomodule/redigo/redis" - "github.com/stretchr/testify/assert" - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestRateLimiterMemoryMiddleware(t *testing.T) { - limit := 60 - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - wrappedHandler := RateLimiter(ctx, limit)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server := httptest.NewServer(wrappedHandler) - defer server.Close() - - for a := 1; a < limit+2; a++ { - resp, _ := http.Get(server.URL) - // The GCRA in throttled divides each minute into equal segments and - // only allows 1 call per segment. This means we can't hit the API 60 - // times in 1 second. In order to verify expected behavior, start querying - // more than 1 second apart and move closer, hitting the first limit on - // iteration 4 - if a > 3 { - assert.Equal(t, resp.StatusCode, 429, "Limiter should trigger immediately after limit is exceeded") - } else { - assert.NotEqual(t, resp.StatusCode, 429, "Limiter should not trigger early") - } - // Sleep to allow the bucket to fill up. Sleep less each iteration so - // that we eventually hit the limit. - // Iteration 1: sleeps 2 sec - // Iteration 2: sleeps 1 sec - // Iteration 3: sleeps 0.66 sec - time.Sleep(time.Duration(2/a) * time.Second) - } -} - -func TestRateLimiterRedisMiddleware(t *testing.T) { - limit := 60 - burst := 2 - mr, _ := miniredis.Run() - pool := &redis.Pool{ - MaxIdle: 1, - IdleTimeout: 5000, - Dial: func() (redis.Conn, error) { - return redis.Dial("tcp", mr.Addr()) - }, - } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - wrappedHandler := RateLimiterRedisStore(ctx, limit, burst, pool, "", 1)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server := httptest.NewServer(wrappedHandler) - defer server.Close() - - for a := 1; a < limit+2; a++ { - resp, _ := http.Get(server.URL) - // The GCRA in throttled divides each minute into equal segments and - // only allows 1 call per segment plus bursts. In order to verify - // expected behavior, start querying more than 1 second apart and move - //closer. Accounting for the burst setting we should this at iteration - // 5 - if a > 5 { - assert.Equal(t, resp.StatusCode, 429, "Limiter should trigger immediately after limit is exceeded") - } else { - assert.NotEqual(t, resp.StatusCode, 429, "Limiter should not trigger early") - } - // Sleep to allow the bucket to fill up. Sleep less each iteration so - // that we eventually hit the limit. - // Iteration 1: sleeps 2 sec - // Iteration 2: sleeps 1 sec - // Iteration 3: sleeps 0.66 sec - time.Sleep(time.Duration(2/a) * time.Second) - } -} diff --git a/libs/middleware/request_host.go b/libs/middleware/request_host.go deleted file mode 100644 index 0822bfc06..000000000 --- a/libs/middleware/request_host.go +++ /dev/null @@ -1,20 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/brave-intl/bat-go/libs/requestutils" -) - -// HostTransfer transfers the request id from header to context -func HostTransfer(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - host := r.Header.Get(requestutils.HostHeaderKey) - if host == "" { - // use the x-forwarded-host - host = r.Header.Get(requestutils.XForwardedHostHeaderKey) - } - r.Header.Set(requestutils.HostHeaderKey, host) - next.ServeHTTP(w, r) - }) -} diff --git a/libs/middleware/request_id.go b/libs/middleware/request_id.go deleted file mode 100644 index 606b9b07b..000000000 --- a/libs/middleware/request_id.go +++ /dev/null @@ -1,26 +0,0 @@ -package middleware - -import ( - "context" - "crypto/sha256" - "net/http" - - "github.com/brave-intl/bat-go/libs/requestutils" - uuid "github.com/satori/go.uuid" - "github.com/shengdoushi/base58" -) - -// RequestIDTransfer transfers the request id from header to context -func RequestIDTransfer(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - reqID := r.Header.Get(requestutils.RequestIDHeaderKey) - if reqID == "" { - // generate one if one does not yet exist - bytes := sha256.Sum256(uuid.NewV4().Bytes()) - reqID = base58.Encode(bytes[:], base58.BitcoinAlphabet)[:16] - } - w.Header().Set(requestutils.RequestIDHeaderKey, reqID) - ctx := context.WithValue(r.Context(), requestutils.RequestID, reqID) - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} diff --git a/libs/middleware/request_logger.go b/libs/middleware/request_logger.go deleted file mode 100644 index eb8550be5..000000000 --- a/libs/middleware/request_logger.go +++ /dev/null @@ -1,123 +0,0 @@ -package middleware - -/* -Copyright 2016-current lg authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * The names of authors or contributors may NOT be used to endorse or -promote products derived from this software without specific prior -written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -import ( - "fmt" - "net/http" - "regexp" - "runtime/debug" - "time" - - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/getsentry/sentry-go" - "github.com/go-chi/chi/middleware" - "github.com/rs/zerolog" - "github.com/rs/zerolog/hlog" -) - -var ipPortRE = regexp.MustCompile(`[0-9]+(?:\.[0-9]+){3}(:[0-9]+)?`) - -// RequestLogger logs at the start and stop of incoming HTTP requests as well as recovers from panics -// Modified version of RequestLogger from github.com/rs/zerolog -// Added support for sending captured panic to Sentry -func RequestLogger(logger *zerolog.Logger) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if r.URL.EscapedPath() == "/metrics" { // Skip logging prometheus metric scrapes - next.ServeHTTP(w, r) - return - } - - ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) - t1 := time.Now().UTC() - // only need to get logger from context once per request - logger := hlog.FromRequest(r) - createSubLog(logger, r, 0). - Msg("request started") - - defer func() { - t2 := time.Now().UTC() - - // Recover and record stack traces in case of a panic - if rec := recover(); rec != nil { - // report the reason for the panic - logger.Error(). - Str("panic", fmt.Sprintf("%+v", rec)). - Str("stacktrace", string(debug.Stack())). - Msg("panic recovered") - - // consolidate these: `http: proxy error: read tcp x.x.x.x:xxxx->x.x.x.x:xxxx: i/o timeout` - // any panic that has an ipaddress/port in it - m := string(ipPortRE.ReplaceAll( - []byte(fmt.Sprint(rec)), []byte("x.x.x.x:xxxx"))) - - // Send panic info to Sentry - event := sentry.NewEvent() - event.Message = m - sentry.CaptureEvent(event) - - handlers.AppError{ - Message: http.StatusText(http.StatusInternalServerError), - Code: http.StatusInternalServerError, - }.ServeHTTP(w, r) - } - - status := ww.Status() - // Log the entry, the request is complete. - createSubLog(logger, r, status). - Int("status", status). - Int("size", ww.BytesWritten()). - Dur("duration", t2.Sub(t1)). - Msg("request complete") - }() - - r = r.WithContext(logger.WithContext(r.Context())) - next.ServeHTTP(ww, r) - } - return http.HandlerFunc(fn) - } -} - -func createSubLog(logger *zerolog.Logger, r *http.Request, status int) (subLog *zerolog.Event) { - if status >= 400 && status <= 499 { - subLog = logger.Warn() - } else if status >= 500 { - subLog = logger.Error() - } else { - subLog = logger.Info() - } - return subLog. - Str("host", r.Host). - Str("http_proto", r.Proto). - Str("http_method", r.Method). - Str("uri", r.URL.EscapedPath()) -} diff --git a/libs/middleware/request_logger_test.go b/libs/middleware/request_logger_test.go deleted file mode 100644 index f4dacd891..000000000 --- a/libs/middleware/request_logger_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package middleware - -import ( - "bytes" - "context" - "net/http" - "net/http/httptest" - "regexp" - "testing" - - "github.com/go-chi/chi" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" -) - -func TestRequestLogger_LogsPanic(t *testing.T) { - buffer := &bytes.Buffer{} - logger := zerolog.New(zerolog.ConsoleWriter{Out: buffer}). - With(). - Timestamp(). - Logger() - - ctx := logger.WithContext(context.Background()) - - panicHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - panic("panicky handler") - }) - - rw := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "/", nil). - WithContext(ctx) - - requestLoggerMiddleware := RequestLogger(nil) - - router := chi.NewRouter() - router.Handle("/", requestLoggerMiddleware(panicHandler)) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, r) - - actual := buffer.String() - - assert.Contains(t, actual, "panic recovered") - assert.Regexp(t, regexp.MustCompile("panic=.+panicky handler"), actual) - assert.Regexp(t, regexp.MustCompile("stacktrace=.+"), actual) -} diff --git a/libs/middleware/response_date.go b/libs/middleware/response_date.go deleted file mode 100644 index 3d8195562..000000000 --- a/libs/middleware/response_date.go +++ /dev/null @@ -1,16 +0,0 @@ -package middleware - -import ( - "net/http" - "time" -) - -// SetResponseDate sets a header date in the response -func SetResponseDate() func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("date", time.Now().Format(http.TimeFormat)) - next.ServeHTTP(w, r) - }) - } -} diff --git a/libs/middleware/simple_token.go b/libs/middleware/simple_token.go deleted file mode 100644 index c144b7bd7..000000000 --- a/libs/middleware/simple_token.go +++ /dev/null @@ -1,64 +0,0 @@ -package middleware - -import ( - "context" - "crypto/subtle" - "net/http" - "os" - "strings" -) - -type bearerTokenKey struct{} - -var ( - // TokenList is the list of tokens that are accepted as valid - TokenList = strings.Split(os.Getenv("TOKEN_LIST"), ",") -) - -// BearerToken is a middleware that adds the bearer token included in a request's headers to context -func BearerToken(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var token string - - bearer := r.Header.Get("Authorization") - - if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" { - token = bearer[7:] - } - ctx := context.WithValue(r.Context(), bearerTokenKey{}, token) - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} - -func isSimpleTokenValid(list []string, token string) bool { - if token == "" { - return false - } - for _, validToken := range list { - // NOTE token length information is leaked even with subtle.ConstantTimeCompare - if subtle.ConstantTimeCompare([]byte(validToken), []byte(token)) == 1 { - return true - } - } - return false -} - -func isSimpleTokenInContext(ctx context.Context) bool { - token, ok := ctx.Value(bearerTokenKey{}).(string) - if !ok || !isSimpleTokenValid(TokenList, token) { - return false - } - return true -} - -// SimpleTokenAuthorizedOnly is a middleware that restricts access to requests with a valid bearer token via context -// NOTE the valid token is populated via BearerToken -func SimpleTokenAuthorizedOnly(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !isSimpleTokenInContext(r.Context()) { - http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) - return - } - next.ServeHTTP(w, r) - }) -} diff --git a/libs/middleware/simple_token_test.go b/libs/middleware/simple_token_test.go deleted file mode 100644 index d979954ac..000000000 --- a/libs/middleware/simple_token_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package middleware - -import ( - "strings" - "testing" -) - -func Test_isSimpleTokenValid(t *testing.T) { - tokenList := strings.Split("", ",") - if isSimpleTokenValid(tokenList, "") { - t.Error("Expected empty token to always be invalid") - } - - if isSimpleTokenValid([]string{}, "") { - t.Error("Expected empty token list to always to be invalid") - } - - tokenList = strings.Split("FOO", ",") - if !isSimpleTokenValid(tokenList, "FOO") { - t.Error("Expected single token to be valid") - } - if isSimpleTokenValid(tokenList, "BAR") { - t.Error("Expected wrong token to be invalid") - } - - tokenList = strings.Split("FOO ", ",") - if isSimpleTokenValid(tokenList, "FOO") { - t.Error("Expected single token to be invalid if list is malformed") - } - - tokenList = strings.Split("FOO,BAR", ",") - if !isSimpleTokenValid(tokenList, "FOO") { - t.Error("Expected multiple tokens to be valid") - } - if !isSimpleTokenValid(tokenList, "BAR") { - t.Error("Expected multiple tokens to be valid") - } - if isSimpleTokenValid(tokenList, "XXX") { - t.Error("Expected wrong tokens to be invalid") - } -} diff --git a/libs/middleware/upgrade_required.go b/libs/middleware/upgrade_required.go deleted file mode 100644 index bba7a0e60..000000000 --- a/libs/middleware/upgrade_required.go +++ /dev/null @@ -1,31 +0,0 @@ -package middleware - -import ( - "errors" - "net/http" - "time" - - "github.com/brave-intl/bat-go/libs/handlers" -) - -var ( - errUpgradeRequired = errors.New("upgrade required, cutoff exceeded") -) - -// NewUpgradeRequiredByMiddleware passes a service into the context -func NewUpgradeRequiredByMiddleware(cutoff time.Time) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if time.Now().Before(cutoff) { - next.ServeHTTP(w, r) - return - } - ae := handlers.AppError{ - Cause: errUpgradeRequired, - Message: "upgrade required, cutoff exceeded", - Code: http.StatusUpgradeRequired, - } - ae.ServeHTTP(w, r) - }) - } -} diff --git a/libs/middleware/upgrade_required_test.go b/libs/middleware/upgrade_required_test.go deleted file mode 100644 index 609b7baa8..000000000 --- a/libs/middleware/upgrade_required_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package middleware - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestUpgradeRequiredByMiddleware(t *testing.T) { - // after cutoff - wrappedHandler := NewUpgradeRequiredByMiddleware(time.Now().Add(-1 * time.Second))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server := httptest.NewServer(wrappedHandler) - defer server.Close() - resp, _ := http.Get(server.URL) - assert.Equal(t, resp.StatusCode, http.StatusUpgradeRequired, "status code should be upgrade required") - - // not yet cutoff - wrappedHandler = NewUpgradeRequiredByMiddleware(time.Now().Add(1 * time.Second))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server = httptest.NewServer(wrappedHandler) - defer server.Close() - resp, _ = http.Get(server.URL) - assert.Equal(t, resp.StatusCode, http.StatusOK, "status code should be OK") -} diff --git a/libs/nitro/attestation.go b/libs/nitro/attestation.go deleted file mode 100644 index 6472e784c..000000000 --- a/libs/nitro/attestation.go +++ /dev/null @@ -1,188 +0,0 @@ -package nitro - -import ( - "bytes" - "context" - "crypto" - "crypto/sha256" - "crypto/x509" - "encoding/hex" - "errors" - "fmt" - "io" - "log" - "time" - - "github.com/brave-intl/bat-go/libs/logging" - "github.com/hf/nitrite" - "github.com/hf/nsm" - "github.com/hf/nsm/request" -) - -// per https://docs.aws.amazon.com/enclaves/latest/user/set-up-attestation.html -// PCR1 is a contiguous measurement of the kernel and boot ramfs data. -// the kernel and boot ramfs are present in /usr/share/nitro_enclaves/blobs/ -// and shipped as part of the official nitro cli tooling. as a result, they -// do not vary based on the docker image we provide and are consistent across -// all images that we produce. for simplicity, we hardcode the current PCR1 value -// corresponding to the latest nitro cli tooling release. -// NOTE: this will need to be changed if the nitro cli tools are updated -const pcr1Hex = "dc9f5af64d83079f2fddca94016f1cba17eb95eb78638eaff32c75517274f05537aabfcbe8e02cb8837906197cf58506" - -// RootAWSNitroCert is the root certificate for the nitro enclaves in aws, -// retrieved from https://aws-nitro-enclaves.amazonaws.com/AWS_NitroEnclaves_Root-G1.zip -var RootAWSNitroCert = `-----BEGIN CERTIFICATE----- -MIICETCCAZagAwIBAgIRAPkxdWgbkK/hHUbMtOTn+FYwCgYIKoZIzj0EAwMwSTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMRswGQYD -VQQDDBJhd3Mubml0cm8tZW5jbGF2ZXMwHhcNMTkxMDI4MTMyODA1WhcNNDkxMDI4 -MTQyODA1WjBJMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQL -DANBV1MxGzAZBgNVBAMMEmF3cy5uaXRyby1lbmNsYXZlczB2MBAGByqGSM49AgEG -BSuBBAAiA2IABPwCVOumCMHzaHDimtqQvkY4MpJzbolL//Zy2YlES1BR5TSksfbb -48C8WBoyt7F2Bw7eEtaaP+ohG2bnUs990d0JX28TcPQXCEPZ3BABIeTPYwEoCWZE -h8l5YoQwTcU/9KNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkCW1DdkF -R+eWw5b6cp3PmanfS5YwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYC -MQCjfy+Rocm9Xue4YnwWmNJVA44fA0P5W2OpYow9OYCVRaEevL8uO1XYru5xtMPW -rfMCMQCi85sWBbJwKKXdS6BptQFuZbT73o/gBh1qUxl/nNr12UO8Yfwr6wPLb+6N -IwLz3/Y= ------END CERTIFICATE-----` - -// ExpectedPCR1 is the expected PCR1 value for all images built with the official nitro cli tooling -var ExpectedPCR1 []byte - -func init() { - var err error - ExpectedPCR1, err = hex.DecodeString(pcr1Hex) - if err != nil { - log.Fatal(err) - } -} - -// Attest takes as input a nonce, user-provided data and a public key, and then -// asks the Nitro hypervisor to return a signed attestation document that -// contains all three values. -func Attest(ctx context.Context, nonce, userData, publicKey []byte) ([]byte, error) { - - var logger = logging.Logger(ctx, "nitro.Attest") - s, err := nsm.OpenDefaultSession() - if err != nil { - return nil, err - } - defer func() { - if err = s.Close(); err != nil { - logger.Error().Msgf("Attestation: Failed to close default NSM session: %s", err) - } - }() - - res, err := s.Send(&request.Attestation{ - Nonce: nonce, - UserData: userData, - PublicKey: publicKey, - }) - if err != nil { - return nil, err - } - - if res.Attestation == nil || res.Attestation.Document == nil { - return nil, errors.New("NSM device did not return an attestation") - } - - return res.Attestation.Document, nil -} - -// GetPCRs returns the PCR values for the currently running enclave by -// performing an attestation and parsing the result -func GetPCRs() (map[uint][]byte, error) { - sig, err := Attest(context.Background(), nil, nil, nil) - if err != nil { - return nil, err - } - verifier := NewVerifier(nil) - _, pcrs, err := verifier.verifySigOnlyNotPCRs(nil, sig, crypto.Hash(0)) - return pcrs, err -} - -// Signer is a placeholder struct to sign using a nitro attestation -type Signer struct{} - -// Sign the message using the nitro signer -func (s Signer) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { - hash := sha256.Sum256(message) - return Attest(context.Background(), nil, hash[:], nil) -} - -// Verifier specifies the PCR values required for verification -type Verifier struct { - PCRs map[uint][]byte - Now func() time.Time -} - -// NewVerifier returns a new verifier for nitro attestations -func NewVerifier(pcrs map[uint][]byte) Verifier { - return Verifier{ - PCRs: pcrs, - Now: time.Now, - } -} - -// verifySigOnlyNotPCRs verifies that a signature is good but does not check the PCR values -func (v Verifier) verifySigOnlyNotPCRs(message, sig []byte, opts crypto.SignerOpts) (bool, map[uint][]byte, error) { - pool := x509.NewCertPool() - ok := pool.AppendCertsFromPEM([]byte(RootAWSNitroCert)) - if !ok { - return false, nil, errors.New("could not create a valid root cert pool") - } - - res, err := nitrite.Verify( - sig, - nitrite.VerifyOptions{ - Roots: pool, - CurrentTime: v.Now(), - }, - ) - if nil != err { - return false, nil, err - } - - var userdata []byte - if message != nil { - hash := sha256.Sum256(message) - userdata = hash[:] - } - if !bytes.Equal(res.Document.UserData, userdata) { - return false, nil, nil - } - - return true, res.Document.PCRs, nil -} - -// Verify the signature sig for message using the nitro verifier -func (v Verifier) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) { - valid, pcrs, err := v.verifySigOnlyNotPCRs(message, sig, opts) - if err != nil || !valid { - fmt.Println("ERR: verify failed or not valid") - return valid, err - } - - if len(v.PCRs) == 0 { - fmt.Println("ERR: pcrs was empty") - return false, nil - } - - for pcr, expectedV := range v.PCRs { - v, exists := pcrs[pcr] - if !exists { - fmt.Println("ERR: pcr was missing", pcr) - return false, nil - } - if !bytes.Equal(expectedV, v) { - fmt.Println("ERR: pcr did not match", pcr) - return false, nil - } - } - return true, nil -} - -// String returns the stringified PCR values we are checking against -func (v Verifier) String() string { - return fmt.Sprint(v.PCRs) -} diff --git a/libs/nitro/aws/sdk.go b/libs/nitro/aws/sdk.go deleted file mode 100644 index 9afc7bca5..000000000 --- a/libs/nitro/aws/sdk.go +++ /dev/null @@ -1,164 +0,0 @@ -package aws - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "net/http" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds" - "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" - "golang.org/x/net/http2" - - smithy "github.com/aws/smithy-go/logging" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/nitro" -) - -// Amazon Root CA 1-4, https://www.amazontrust.com/repository/ -const amazonRoots = ` ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- - ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK -gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ -W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg -1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K -8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r -2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me -z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR -8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj -mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz -7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 -+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI -0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB -Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm -UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 -LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY -+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS -k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl -7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm -btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl -urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ -fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 -n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE -76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H -9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT -4PsJYGw= ------END CERTIFICATE----- - ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl -ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr -ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr -BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM -YyRIHN8wfdVoOw== ------END CERTIFICATE----- - ------BEGIN CERTIFICATE----- -MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi -9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk -M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB -MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw -CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW -1KyLa2tJElMzrdfkviT8tQp21KW8EA== ------END CERTIFICATE----- -` - -// NewAWSConfig creates a new AWS SDK config that communicates via an HTTP -// proxy listening on a vsock address, it automatically retrieves any EC2 -// role credentials of the instance hosting the enclave -func NewAWSConfig(ctx context.Context, proxyAddr string, region string) (aws.Config, error) { - logger := logging.Logger(ctx, "aws.NewAWSConfig") - - logger.Info(). - Str("proxyAddr", proxyAddr). - Str("region", region). - Msg("setting up new aws config") - - var client http.Client - tr := nitro.NewProxyRoundTripper(ctx, proxyAddr).(*http.Transport) - - certs := x509.NewCertPool() - certs.AppendCertsFromPEM([]byte(amazonRoots)) - tr.TLSClientConfig = &tls.Config{ - RootCAs: certs, - } - - logger.Info(). - Str("transport", fmt.Sprintf("%+v", tr)). - Msg("transport is setup") - // So client makes HTTP/2 requests - err := http2.ConfigureTransport(tr) - if err != nil { - return aws.Config{}, fmt.Errorf("failed to configure transport for HTTP/2, %v", err) - } - - client = http.Client{ - Transport: tr, - } - - var applicationLogger smithy.Logger - - if logWriter, ok := ctx.Value(appctx.LogWriterCTXKey).(io.Writer); ok { - applicationLogger = smithy.NewStandardLogger(logWriter) - } - - cfg, err := config.LoadDefaultConfig(context.TODO(), - config.WithHTTPClient(&client), - config.WithRegion("us-west-2"), - config.WithLogger(applicationLogger), - ) - if err != nil { - return aws.Config{}, fmt.Errorf("unable to load SDK config, %v", err) - } - - provider := ec2rolecreds.New(func(options *ec2rolecreds.Options) { - options.Client = imds.NewFromConfig(cfg) - }) - - return config.LoadDefaultConfig(context.TODO(), - config.WithHTTPClient(&client), - config.WithRegion(region), - config.WithCredentialsProvider(provider), - config.WithLogger(applicationLogger), - ) -} diff --git a/libs/nitro/ber/ber.go b/libs/nitro/ber/ber.go deleted file mode 100644 index 1082abcd5..000000000 --- a/libs/nitro/ber/ber.go +++ /dev/null @@ -1,301 +0,0 @@ -package ber - -/* -https://github.com/mozilla-services/pkcs7/blob/master/ber.go - -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import ( - "bytes" - "errors" -) - -var encodeIndent = 0 - -type asn1Object interface { - EncodeTo(writer *bytes.Buffer) error -} - -type asn1Structured struct { - tagBytes []byte - content []asn1Object -} - -func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { - //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes) - encodeIndent++ - inner := new(bytes.Buffer) - for _, obj := range s.content { - err := obj.EncodeTo(inner) - if err != nil { - return err - } - } - encodeIndent-- - out.Write(s.tagBytes) - encodeLength(out, inner.Len()) - out.Write(inner.Bytes()) - return nil -} - -type asn1Primitive struct { - tagBytes []byte - length int - content []byte -} - -func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error { - _, err := out.Write(p.tagBytes) - if err != nil { - return err - } - if err = encodeLength(out, p.length); err != nil { - return err - } - //fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length) - //fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content)) - out.Write(p.content) - - return nil -} - -func ber2der(ber []byte) ([]byte, error) { - if len(ber) == 0 { - return nil, errors.New("ber2der: input ber is empty") - } - //fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber)) - out := new(bytes.Buffer) - - obj, _, err := readObject(ber, 0) - if err != nil { - return nil, err - } - obj.EncodeTo(out) - - // if offset < len(ber) { - // return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber)) - //} - - return out.Bytes(), nil -} - -// encodes lengths that are longer than 127 into string of bytes -func marshalLongLength(out *bytes.Buffer, i int) (err error) { - n := lengthLength(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -// computes the byte length of an encoded length value -func lengthLength(i int) (numBytes int) { - numBytes = 1 - for i > 255 { - numBytes++ - i >>= 8 - } - return -} - -// encodes the length in DER format -// If the length fits in 7 bits, the value is encoded directly. -// -// Otherwise, the number of bytes to encode the length is first determined. -// This number is likely to be 4 or less for a 32bit length. This number is -// added to 0x80. The length is encoded in big endian encoding follow after -// -// Examples: -// length | byte 1 | bytes n -// 0 | 0x00 | - -// 120 | 0x78 | - -// 200 | 0x81 | 0xC8 -// 500 | 0x82 | 0x01 0xF4 -// -func encodeLength(out *bytes.Buffer, length int) (err error) { - if length >= 128 { - l := lengthLength(length) - err = out.WriteByte(0x80 | byte(l)) - if err != nil { - return - } - err = marshalLongLength(out, length) - if err != nil { - return - } - } else { - err = out.WriteByte(byte(length)) - if err != nil { - return - } - } - return -} - -func readObject(ber []byte, offset int) (asn1Object, int, error) { - berLen := len(ber) - if offset >= berLen { - return nil, 0, errors.New("ber2der: offset is after end of ber data") - } - tagStart := offset - b := ber[offset] - offset++ - if offset >= berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - tag := b & 0x1F // last 5 bits - if tag == 0x1F { - tag = 0 - for ber[offset] >= 0x80 { - tag = tag*128 + ber[offset] - 0x80 - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - } - // jvehent 20170227: this doesn't appear to be used anywhere... - //tag = tag*128 + ber[offset] - 0x80 - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - } - tagEnd := offset - - kind := b & 0x20 - if kind == 0 { - debugprint("--> Primitive\n") - } else { - debugprint("--> Constructed\n") - } - // read length - var length int - l := ber[offset] - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - indefinite := false - if l > 0x80 { - numberOfBytes := (int)(l & 0x7F) - if numberOfBytes > 4 { // int is only guaranteed to be 32bit - return nil, 0, errors.New("ber2der: BER tag length too long") - } - if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F { - return nil, 0, errors.New("ber2der: BER tag length is negative") - } - if (int)(ber[offset]) == 0x0 { - return nil, 0, errors.New("ber2der: BER tag length has leading zero") - } - debugprint("--> (compute length) indicator byte: %x\n", l) - debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) - for i := 0; i < numberOfBytes; i++ { - length = length*256 + (int)(ber[offset]) - offset++ - if offset > berLen { - return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") - } - } - } else if l == 0x80 { - indefinite = true - } else { - length = (int)(l) - } - if length < 0 { - return nil, 0, errors.New("ber2der: invalid negative value found in BER tag length") - } - //fmt.Printf("--> length : %d\n", length) - contentEnd := offset + length - if contentEnd > len(ber) { - return nil, 0, errors.New("ber2der: BER tag length is more than available data") - } - debugprint("--> content start : %d\n", offset) - debugprint("--> content end : %d\n", contentEnd) - debugprint("--> content : % X\n", ber[offset:contentEnd]) - var obj asn1Object - if indefinite && kind == 0 { - return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding") - } - if kind == 0 { - obj = asn1Primitive{ - tagBytes: ber[tagStart:tagEnd], - length: length, - content: ber[offset:contentEnd], - } - } else { - var subObjects []asn1Object - for (offset < contentEnd) || indefinite { - var subObj asn1Object - var err error - subObj, offset, err = readObject(ber, offset) - if err != nil { - return nil, 0, err - } - subObjects = append(subObjects, subObj) - - if indefinite { - terminated, err := isIndefiniteTermination(ber, offset) - if err != nil { - return nil, 0, err - } - - if terminated { - break - } - } - } - obj = asn1Structured{ - tagBytes: ber[tagStart:tagEnd], - content: subObjects, - } - } - - // Apply indefinite form length with 0x0000 terminator. - if indefinite { - contentEnd = offset + 2 - } - - return obj, contentEnd, nil -} - -func isIndefiniteTermination(ber []byte, offset int) (bool, error) { - if len(ber)-offset < 2 { - return false, errors.New("ber2der: Invalid BER format") - } - - return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil -} - -func debugprint(format string, a ...interface{}) { - //fmt.Printf(format, a) -} - -func ToDER(ber []byte) ([]byte, error) { - return ber2der(ber) -} diff --git a/libs/nitro/ber/ber_test.go b/libs/nitro/ber/ber_test.go deleted file mode 100644 index 12c1e687c..000000000 --- a/libs/nitro/ber/ber_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package ber - -/* -https://github.com/mozilla-services/pkcs7/blob/master/ber_test.go - -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import ( - "bytes" - "encoding/asn1" - "encoding/pem" - "fmt" - "strings" - "testing" -) - -func TestBer2Der(t *testing.T) { - // indefinite length fixture - ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00} - expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01} - der, err := ber2der(ber) - if err != nil { - t.Fatalf("ber2der failed with error: %v", err) - } - if !bytes.Equal(der, expected) { - t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) - } - - if der2, err := ber2der(der); err != nil { - t.Errorf("ber2der on DER bytes failed with error: %v", err) - } else { - if !bytes.Equal(der, der2) { - t.Error("ber2der is not idempotent") - } - } - var thing struct { - Number int - } - rest, err := asn1.Unmarshal(der, &thing) - if err != nil { - t.Errorf("Cannot parse resulting DER because: %v", err) - } else if len(rest) > 0 { - t.Errorf("Resulting DER has trailing data: % X", rest) - } -} - -func TestBer2Der_Negatives(t *testing.T) { - fixtures := []struct { - Input []byte - ErrorContains string - }{ - {[]byte{0x30, 0x85}, "tag length too long"}, - {[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"}, - {[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"}, - {[]byte{0x30, 0x80, 0x1, 0x2, 0x1, 0x2}, "Invalid BER format"}, - {[]byte{0x30, 0x80, 0x1, 0x2}, "BER tag length is more than available data"}, - {[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"}, - {[]byte{0x30}, "end of ber data reached"}, - } - - for _, fixture := range fixtures { - _, err := ber2der(fixture.Input) - if err == nil { - t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains) - } - if !strings.Contains(err.Error(), fixture.ErrorContains) { - t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error()) - } - } -} - -func TestBer2Der_NestedMultipleIndefinite(t *testing.T) { - // indefinite length fixture - ber := []byte{0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00} - expected := []byte{0x30, 0x0A, 0x30, 0x03, 0x02, 0x01, 0x01, 0x30, 0x03, 0x02, 0x01, 0x02} - - der, err := ber2der(ber) - if err != nil { - t.Fatalf("ber2der failed with error: %v", err) - } - if bytes.Compare(der, expected) != 0 { - t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) - } - - if der2, err := ber2der(der); err != nil { - t.Errorf("ber2der on DER bytes failed with error: %v", err) - } else { - if !bytes.Equal(der, der2) { - t.Error("ber2der is not idempotent") - } - } - var thing struct { - Nest1 struct { - Number int - } - Nest2 struct { - Number int - } - } - rest, err := asn1.Unmarshal(der, &thing) - if err != nil { - t.Errorf("Cannot parse resulting DER because: %v", err) - } else if len(rest) > 0 { - t.Errorf("Resulting DER has trailing data: % X", rest) - } -} - -func TestVerifyIndefiniteLengthBer(t *testing.T) { - decoded := mustDecodePEM([]byte(testPKCS7)) - - _, err := ber2der(decoded) - if err != nil { - t.Errorf("cannot parse indefinite length ber: %v", err) - } -} - -func mustDecodePEM(data []byte) []byte { - var block *pem.Block - block, rest := pem.Decode(data) - if len(rest) != 0 { - panic(fmt.Errorf("unexpected remaining PEM block during decode")) - } - return block.Bytes -} - -const testPKCS7 = ` ------BEGIN PKCS7----- -MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0B -BwGggCSABIIDfXsiQWdlbnRBY3Rpb25PdmVycmlkZXMiOnsiQWdlbnRPdmVycmlk -ZXMiOnsiRmlsZUV4aXN0c0JlaGF2aW9yIjoiT1ZFUldSSVRFIn19LCJBcHBsaWNh -dGlvbklkIjoiZTA0NDIzZTQtN2E2Ny00ZjljLWIyOTEtOTllNjNjMWMyMTU4Iiwi -QXBwbGljYXRpb25OYW1lIjoibWthbmlhLXhyZF9zYW0uY2R3c19lY2hvc2VydmVy -IiwiRGVwbG95bWVudENyZWF0b3IiOiJ1c2VyIiwiRGVwbG95bWVudEdyb3VwSWQi -OiJmYWI5MjEwZi1mNmM3LTQyODUtYWEyZC03Mzc2MGQ4ODE3NmEiLCJEZXBsb3lt -ZW50R3JvdXBOYW1lIjoibWthbmlhLXhyZF9zYW0uY2R3c19lY2hvc2VydmVyX2Rn -IiwiRGVwbG95bWVudElkIjoiZC1UREUxVTNXREEiLCJEZXBsb3ltZW50VHlwZSI6 -IklOX1BMQUNFIiwiR2l0SHViQWNjZXNzVG9rZW4iOm51bGwsIkluc3RhbmNlR3Jv -dXBJZCI6ImZhYjkyMTBmLWY2YzctNDI4NS1hYTJkLTczNzYwZDg4MTc2YSIsIlJl -dmlzaW9uIjp7IkFwcFNwZWNDb250ZW50IjpudWxsLCJDb2RlQ29tbWl0UmV2aXNp -b24iOm51bGwsIkdpdEh1YlJldmlzaW9uIjpudWxsLCJHaXRSZXZpc2lvbiI6bnVs -bCwiUmV2aXNpb25UeXBlIjoiUzMiLCJTM1JldmlzaW9uIjp7IkJ1Y2tldCI6Im1r -YW5pYS1jZHdzLWRlcGxveS1idWNrZXQiLCJCdW5kbGVUeXBlIjoiemlwIiwiRVRh -ZyI6bnVsbCwiS2V5IjoieHJkOjpzYW0uY2R3czo6ZWNob3NlcnZlcjo6MTo6Lnpp -cCIsIlZlcnNpb24iOm51bGx9fSwiUzNSZXZpc2lvbiI6eyJCdWNrZXQiOiJta2Fu -aWEtY2R3cy1kZXBsb3ktYnVja2V0IiwiQnVuZGxlVHlwZSI6InppcCIsIkVUYWci -Om51bGwsIktleSI6InhyZDo6c2FtLmNkd3M6OmVjaG9zZXJ2ZXI6OjE6Oi56aXAi -LCJWZXJzaW9uIjpudWxsfSwiVGFyZ2V0UmV2aXNpb24iOm51bGx9AAAAAAAAoIAw -ggWbMIIEg6ADAgECAhAGrjFMK45t2jcNHtjY1DjEMA0GCSqGSIb3DQEBCwUAMEYx -CzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xFTATBgNVBAsTDFNlcnZlciBD -QSAxQjEPMA0GA1UEAxMGQW1hem9uMB4XDTIwMTExMjAwMDAwMFoXDTIxMTAxNTIz -NTk1OVowNDEyMDAGA1UEAxMpY29kZWRlcGxveS1zaWduZXItdXMtZWFzdC0yLmFt -YXpvbmF3cy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDit4f+ -I4BSv4rBV/8bJ+f4KqBwTCt9iJeau/r9liQfMgj/C1M2E+aa++u8BtY/LQstB44v -v6KqcaiOyWpkD9OsUty9qb4eNTPF2Y4jpNsi/Hfw0phsd9gLun2foppILmL4lZIG -lBhTeEwv6qV4KbyXOG9abHOX32+jVFtM1rbzHNFvz90ysfZp16TBAi7IRKEZeXvd -MvlJJMAJtAoblxiDIS3A1csY1G4XHYET8xIoCop3mqEZEtAxUUP2epdXXdhD2U0G -7alSRS54o91QW1Dp3A13lu1A1nds9CkWlPkDTpKSUG/qN5y5+6dCCGaydgL5krMs -R79bCrR1sEKm5hi1AgMBAAGjggKVMIICkTAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8 -o5QHJ5Z0W/k90DAdBgNVHQ4EFgQUPF5qTbnTDYhmp7tGmmL/jTmLoHMwNAYDVR0R -BC0wK4IpY29kZWRlcGxveS1zaWduZXItdXMtZWFzdC0yLmFtYXpvbmF3cy5jb20w -DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7 -BgNVHR8ENDAyMDCgLqAshipodHRwOi8vY3JsLnNjYTFiLmFtYXpvbnRydXN0LmNv -bS9zY2ExYi5jcmwwIAYDVR0gBBkwFzALBglghkgBhv1sAQIwCAYGZ4EMAQIBMHUG -CCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3Auc2NhMWIuYW1h -em9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8vY3J0LnNjYTFiLmFtYXpv -bnRydXN0LmNvbS9zY2ExYi5jcnQwDAYDVR0TAQH/BAIwADCCAQQGCisGAQQB1nkC -BAIEgfUEgfIA8AB2APZclC/RdzAiFFQYCDCUVo7jTRMZM7/fDC8gC8xO8WTjAAAB -dboejIcAAAQDAEcwRQIgeqoKXbST17TCEzM1BMWx/jjyVQVBIN3LG17U4OaV364C -IQDPUSJZhJm7uqGea6+VwqeDe/vGuGSuJzkDwTIOeIXPaAB2AFzcQ5L+5qtFRLFe -mtRW5hA3+9X6R9yhc5SyXub2xw7KAAABdboejNQAAAQDAEcwRQIgEKIAwwhjUcq2 -iwzBAagdy+fTiKnBY1Yjf6wOeRpwXfMCIQC8wM3nxiWrGgIpdzzgDvFhZZTV3N81 -JWcYAu+srIVOhTANBgkqhkiG9w0BAQsFAAOCAQEAer9kml53XFy4ZSVzCbdsIFYP -Ohu7LDf5iffHBVZFnGOEVOmiPYYkNwi9R6EHIYaAs7G7GGLCp/6tdc+G4eF1j6wB -IkmXZcxMTxk/87R+S+36yDLg1GBZvqttLfexj0TRVAfVLJc7FjLXAW2+wi7YyNe8 -X17lWBwHxa1r5KgweJshGzYVUsgMTSx0aJ+93ZnqplBp9x+9DSQNqqNlBgxFANxs -ux+dfpduyLd8VLqtlECGC07tYE4mBaAjMiNjCZRWMp8ya/Z6J/bJZ27IDGA4dXzm -l9NNnlbuUDAenAByUqE+0b78J6EmmdAVf+N8siriMg02FdP3lAXJLE8tDeZp8AAA -MYICIDCCAhwCAQEwWjBGMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUw -EwYDVQQLEwxTZXJ2ZXIgQ0EgMUIxDzANBgNVBAMTBkFtYXpvbgIQBq4xTCuObdo3 -DR7Y2NQ4xDANBglghkgBZQMEAgEFAKCBmDAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN -AQcBMBwGCSqGSIb3DQEJBTEPFw0yMTA2MjQxOTU1MzFaMC0GCSqGSIb3DQEJNDEg -MB4wDQYJYIZIAWUDBAIBBQChDQYJKoZIhvcNAQELBQAwLwYJKoZIhvcNAQkEMSIE -IP7gMuT2H0/AhgPgj3Eo0NWCIdQOBjJO18coNKIaOnJYMA0GCSqGSIb3DQEBCwUA -BIIBAJX+e87q0YvRon9/ENTvE0FoYMzYblID2Reek6L217ZlZ6pUuRsc4ghhJ5Yh -WZeOCaLwi4mrnQ5/+DGKkJ4a/w5sqFTwtJIGIIAuDCn/uDm8kIDUVkbeznSOLoPA -67cxiqgIdqZ5pqUoid2YsDj20owrGDG4wUF6ZvhM9g/5va3CAhxqvTE2HwjhHTfz -Cgl8Nlvalz7YxXEf2clFEiEVa1fVaGMl9pCyedAmTfd6hoivcpAsopvXfVaaaR2y -iuZidpUfFhSk+Ls7TU/kB74ckfUGj5q/5HcKJgb/S+FYUV7eu0ewzTyW1uRl/d0U -Tb7e7EjgDGJsjOTMdTrMfv8ho8kAAAAAAAA= ------END PKCS7----- -` diff --git a/libs/nitro/handlers.go b/libs/nitro/handlers.go deleted file mode 100644 index 3497152e1..000000000 --- a/libs/nitro/handlers.go +++ /dev/null @@ -1,14 +0,0 @@ -package nitro - -import ( - "fmt" - "net/http" - - "github.com/brave-intl/bat-go/libs/logging" -) - -// EnclaveHealthCheck - status check handler for nitro enclave service -func EnclaveHealthCheck(w http.ResponseWriter, r *http.Request) { - logging.Logger(r.Context(), "health-check").Trace().Msg("in health-check handler") - fmt.Fprintf(w, "OK\n") -} diff --git a/libs/nitro/kms.go b/libs/nitro/kms.go deleted file mode 100644 index 7d184de07..000000000 --- a/libs/nitro/kms.go +++ /dev/null @@ -1,23 +0,0 @@ -package nitro - -import ( - "github.com/brave-intl/bat-go/libs/nitro/ber" - "github.com/brave-intl/bat-go/libs/nitro/pkcs7" - - "crypto/rsa" -) - -// Decrypt the encrypted response from KMS -func Decrypt(key *rsa.PrivateKey, b []byte) ([]byte, error) { - der, err := ber.ToDER(b) - if err != nil { - return nil, err - } - - pkcs, err := pkcs7.Parse(der) - if err != nil { - return nil, err - } - - return pkcs.Decrypt(key) -} diff --git a/libs/nitro/kms_test.go b/libs/nitro/kms_test.go deleted file mode 100644 index f77451b3f..000000000 --- a/libs/nitro/kms_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package nitro - -import ( - "crypto/x509" - "encoding/base64" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestDecrypt(t *testing.T) { - keyb64 := "MIIJKQIBAAKCAgEAxnHVZ3EQuEGY1spzsmkLxCMNm5ho1815GriHS5z5TKsZjJxMbVRqvKpgJFo2YxRRBAJ/4aLiKcDYENcXb95kWPED+OXmaRShcAh2kpUxhykIuaDVZ8zIfQSNmBbZwoz4ZHTTqo2kDTzIHd6uKMu9sg9oAGYQP0LNq+hLtEfIldBim6vhhR++ac8t/XqbSSF4wpXed3WPHYYqmZaC01t3/C6ZuC79bPRIQ7VlgkAhQYK19gog0N5iWyMpiePR99kwdUdpfz8YzmxJXSG4NC+rwil87aoTcyEoNpoRcqFJ7SLSiw/VJeH4h0pmXzxpWkpvIntM4SSCNAijFwFkoWc/kozg78aiiWy8z+9NCWvskRZHuR68eFqd0Qle9puoCqHzctmvXZ72uqUJmQenvsXZqBnoqgVlExT95T9WYg4IANXNivubVrDJfEJfoqiBakcn9WNziMsBLebr6PDIv47UkEM0YrknOS1EeXM110gQ+citAIRm9OMAGsAGRnsjmKpahmKoZ/CApijes5iMiFyxCw6j4smJ3yz8jpYSlX4/DljTDD/HL2KDB3COYN7jx5p1XxHEtdJbUdO9G/CRvFjRYssevhRWKnxYfQQYMrUBiZ5vwkcMlbiFTzmM3qxrE8Gjc4hH2EgijopunHaJjrpBxvjecbh52KvjtbV+V5jDDmcCAwEAAQKCAgBI9MS+OOUquIMWRGvwpYn7298+2vOAkzv4xOqIs/c7ZpQIC9ZVQ3nSTtj1xGzGVEvMq5aUP6viKdHFry4gpKRDxqftM6hX7YvEgngFP/BrfnyEskqC1NjIjYn45j28ttMDK6g9xcEQVM5FnP5uZSkic2OK8fr7JvYyQz7B0ro9ydTW4oQQyzDX+aEw1YiNhE6/SxX02TV6Zz72JE2IotIj1oYRHsVw7wY6TlA5aJZRXVrET8jswTVryCuXVDh3ZFPaahyw+WOHUGxQXvs3xGbjBRm2zpZD92avXEkZ2Y4yLmE1WubD/KXAJ2wnizA4T7E3/hivlRqBQsOiy5JRfIH0GqR/6Pdxqa1ahAnDiWXup3VzUYyZ47jtCIhbwTlE+PapSZwNaCDVYkoZlbSFSnYNMZNC3CScPMHvkMwNwWx22BNV2un5yZi6adYQ4mCJnBVXbbDTHYM7rwUgqleSrYFgvdjr9TsPDz5hokY9yM8i+cu4Sza2bIEU+fopvjPaVan16X9H3DzeyZiwYibCD//MBa86KCT/0nn8sRxQN4SctPrIXMurDmG11Bt3GkyhgN+wH0jQMP2HXbuYFskG4QNw942K6n6aT3KZBk7jnwBW4RkqTRfS8wkV/Lna8gEyBMq1zqQGlbcFG1Nl0E8rhnj531pCFUW+kdWuZZs+xUh2kQKCAQEAz1gisxG23OYgLp00BECVu7zZhMcB3OufaraLGBH9VZKLWN49rKTlQP94QgphwyHDXfehYzPM4c5CR/NvtEuhpfjyol6NoB5Y+K+jG4aUUPlwLQ0GbZaOCaRFys5o0sFjJAuNKEgKXVykxz3i/t78XMk4cbyX2VPwITvptCbj19TjxQ2L9X3OcLPl27kq3eOlZ13pgYlKMeWrXyRKrc91KfxIhyRuOaOvWudPtQ9Koyzpit4Y6/tnohuFBxFHiQtbe8yE2vBLJfu3egqnMJptBg10CBFCPOTXP/yZMrFzuKbsby2NR+FQxpAdk5jz/q3TAX/LH4bwbTc6iiBlTkX1fQKCAQEA9QMRfHaM+iRf9wqpPCLP1wZDuKUUZadORUlSnQL6Vz3BBDEqGik606v9y9xxvYOEAfSvImvV4OzSpNAJ8ZY0K7+fOyC6UnQOtjTo9QbHSt/hAIHpzoxg0AhLSOkYGWIud/0PpAzC4TAX0Ks4MKmxrRlUsjjJFOXYQ86CV/WqtmI8r0W3iqzgBfK5j/RdRleg98xGrkm5pI+eRH+8L2+zxWwuVmVtpW6x7pTHcwSqCu0+OU6IDj4qdke0FaBbNG6QNrVJWkbnB4PNlVvfpcQPmJ4GWqtXG4iSmm2zcj5aYJ028xaKOQoROKIRyqP6cm4ElJOcrbu47OEi7N/NRIaIswKCAQEAtCBXFMWg3Mq2zogXNlJ9QflP5n+UaL3xYh1vPNvz/zEuCWgYoDEoUI0fhg/K2mAsfyevdqT7qncrMaKUOkcyCSP6dYA6/Q0gMfszcag6lRX9k8Wq/e07O7kQQJ2MhvsmC5xVZtce2fiv1b83wj8pcdoFRRmXuV+oTtbjsnPOoCP8M6YPHpfKjjJaz8VPLjCj8uatArGoWSyy8hZvIgwGNxFKWM0XTM+ceRZWMm80YgmUYeWMNQXVBAlwSbGDOdU6BpSoHiN+xWKmBEnY8OpXKFjrok6MAqazMI1a8aUjaRjqcbNa90UQjU+Jwj2wSmHr23OPnLX3jIncOz7lg1hXaQKCAQEAtNQz5qlUeRW5ognDR7flr6xW9QMZX8EwOmDYUTuuag8Q0rg6e55sZWct5HTpmVZkalEdpQjzjJMdEDje7efOz28Y5TnNaidv6Dh6qsT6957uYEKBvkjK9x73+XLXr3xQEYy7K3ejqTGmkgKccQX9NWVZpkPU3NCaYckbWDHfccZlRXLt+LKIkLtJCx7ubIkmK10f2WheVD8PLz0bNeN6Sb4DIQ6w8wUU26IA5jnOCkx4vTDhp6loC232UnPMfJERbN81qSRlRJ2ZcJ+jG6PtYtVWmTqWCqMsmD9YznuIln/R222KTHjKpwKuoOGPaT8bsFCVtX+0B1hOP4cZ53ZnkwKCAQA4oCngGSSkf9NUlGRzKW38E6azoFs9vVy5gpIanRAJxd+eXcH14QOHlnKhONZe2HgQA0/9cI3n9ZZrcHWxozuVivCyLEuHLamDPVbvnf/OpgRe/LtTXM/NFXlJ2AcMQaSJzshtyOiUYhRi8EkkoNOzlHrETuE12jhtO6QWlVxlucVXOuueenS0QHYg/JM3fqoxwTfEoOXkxM/GjAO1Tf3yqdKMYcCTDOTCrkSQIWKx0u1y30gR1+RfIO2ZSbCV+VKzCnO9M64MU37e9ukQgVsy5s83Y8v0aGX+o5CocBDwkQYeyuMdkQGIMO9B1pDRiFcDe4eVycp0kMEezIDDHGqJ" - ciphertextb64 := "MIAGCSqGSIb3DQEHA6CAMIACAQIxggJrMIICZwIBAoAg4AcF3YTmZkRYgglEmGNb2FwLdoccNa8MvC9FpfUihtcwPAYJKoZIhvcNAQEHMC+gDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEFAASCAgBKgUw9pm3a76LO6vjUHfEocy5u8b3X6Zu37hGH/amHL1szxcAiJ7bCAF7kWWlCp2FxRDjVn/rFd3cfVwhUu2T8FMyMBvE/avPpoYo/nEGcP2MZtDhu27aYZdObEFgAF/EpRK5PPVwhGfFqpkn7h0vzH1it+iOTSewSLYOP2q0U1qTFL0G9b/cL9SQIE7Ym15l8kqU2ZC/f6fLcIUdeKRMnDeQveOjv1ASj+cqs2KLGqp8CMoX0ds3MTHs8VadM5qzz40J/2uJHEpGeXis2c1jayPoTtCc5p5PtkoamrPEbbkvbSj1WrkiCQjz5iLXuIK9GQrFo/OXv6u+qM8Fv6VJugjhlvs8/1ugJnSmK/1eDTBURMiQOv8t6G0nLQw26tXiDxwiF+lSQ+VcT9R23+jMdtFyOsjI6VpjQJiXpv8iuIOnesxben0PBz1nPg4dlCKpd8w8x6yaM3ckfYp3MADryYY3j42FggYiF9J8yryf8ah15jVBRi0NRplH7jtmJK5vqKFFBaERSMBxb9/DpZvgosQ4LO7XoCKkP2mZ9Vnb3SOzsqPP+ZesSS+EE9FZAm8BKcKqc5RXqn1loHUOtg2Q5lMPF5DdL4TJvxFvM7EuRrt3Ox2moHHaAGR6MeOG2mP70GA57thl66Y7ewBYv/elV+TAvNS3QvKNuBZkHi9o73jCABgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBD+sYJrxYPmjywuDomBc195oIAEgeDbFf8PNscwluAONk2Qk9acZsnj1kE8rbUOsiRBnI099DYG702V4swiB+Xfu9P34jRqGT2h65oDoMg8uUwpDP9FEkhpwXYxiopxEeFyNw9KHoTda9vWdy1/OtWVltG3hatJ/mvtog0n7XwqqWaTPTiqYxhkI5NPz2NyrbCuhWbj6r+1cHa2ecSKMrSkr+SjN4ZwA59Gdhd/NT9iE2e2Q0umpowsO5w34zUIlwrVK5RtThlf0SXCKqMeqgKAFd+SLnYc8dBnEFCiwJxTM5qhq61JvarpB6I0dj2jjtBfEUyxmgAAAAAAAAAAAAA=" - expected := []byte{97, 103, 101, 45, 101, 110, 99, 114, 121, 112, 116, 105, 111, 110, 46, 111, 114, 103, 47, 118, 49, 10, 45, 62, 32, 88, 50, 53, 53, 49, 57, 32, 55, 88, 68, 79, 110, 113, 88, 90, 51, 76, 50, 100, 79, 52, 57, 98, 81, 107, 79, 50, 71, 65, 56, 104, 111, 85, 76, 102, 67, 110, 106, 86, 87, 90, 111, 109, 57, 84, 106, 53, 72, 48, 81, 10, 119, 78, 88, 53, 84, 117, 111, 98, 101, 87, 100, 76, 110, 43, 106, 51, 100, 103, 109, 76, 55, 119, 66, 69, 49, 118, 48, 71, 107, 120, 90, 80, 76, 57, 51, 112, 57, 71, 78, 54, 110, 67, 65, 10, 45, 45, 45, 32, 112, 82, 53, 102, 43, 115, 103, 100, 98, 98, 114, 115, 105, 105, 81, 97, 114, 120, 84, 90, 103, 54, 66, 55, 98, 55, 52, 90, 67, 98, 122, 74, 100, 70, 81, 55, 113, 86, 112, 74, 112, 105, 48, 10, 188, 113, 21, 3, 248, 234, 246, 179, 182, 95, 81, 165, 193, 135, 15, 101, 18, 250, 86, 162, 255, 241, 196, 59, 143, 241, 91, 61, 150, 30, 158, 239, 180, 108, 91, 206, 23, 87, 34, 229, 132, 128, 95, 128, 253, 178, 21, 16, 202, 113, 198, 142, 232, 137, 86} - - keybytes, err := base64.StdEncoding.DecodeString(keyb64) - if err != nil { - t.Error("failed to b64decode RSA key") - } - key, err := x509.ParsePKCS1PrivateKey(keybytes) - if err != nil { - t.Error("failed to PKCS decode RSA key") - } - - ciphertext, err := base64.StdEncoding.DecodeString(ciphertextb64) - if err != nil { - t.Error("failed to b64decode ciphertext") - } - - plaintext, err := Decrypt(key, ciphertext) - if err != nil { - t.Error("failed to decrypt ciphertext", err) - } - - assert.Equal(t, expected, plaintext, "failed to decrypt ciphertext, plaintext did not match") -} diff --git a/libs/nitro/log.go b/libs/nitro/log.go deleted file mode 100644 index 8a58ccae0..000000000 --- a/libs/nitro/log.go +++ /dev/null @@ -1,118 +0,0 @@ -package nitro - -import ( - "context" - "net" - "os" - - "github.com/brave-intl/bat-go/libs/closers" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/mdlayher/vsock" -) - -// VsockWriter - structure definition of a vsock writer -type VsockWriter struct { - socket net.Conn - addr string -} - -// NewVsockWriter - create a new vsock writer -func NewVsockWriter(addr string) *VsockWriter { - return &VsockWriter{ - socket: nil, - addr: addr, - } -} - -// Connect - interface implementation for connect method for VsockWriter -func (w *VsockWriter) Connect() error { - if w.socket == nil { - s, err := DialContext(context.Background(), "tcp", w.addr) - if err != nil { - return err - } - w.socket = s - } - return nil -} - -// Close - interface implementation of closer for VsockWriter -func (w VsockWriter) Close() error { - if w.socket != nil { - return w.socket.Close() - } - return nil -} - -// Write -interface implementation of writer for VsockWriter -func (w VsockWriter) Write(p []byte) (n int, err error) { - if w.socket == nil { - err = w.Connect() - if err != nil { - return -1, err - } - } - n, err = w.socket.Write(p) - if err != nil { - // Our socket must have disconnected, reset our connection - w.socket = nil - return n, err - } - return n, nil -} - -// VsockLogServer - implementation of a log server over vsock -type VsockLogServer struct { - baseCtx context.Context - port uint32 -} - -// NewVsockLogServer - create a new VsockLogServer -func NewVsockLogServer(ctx context.Context, port uint32) VsockLogServer { - return VsockLogServer{ - baseCtx: ctx, - port: port, - } -} - -// Serve - interface implementation for Serve for VsockLogServer -func (s VsockLogServer) Serve(l net.Listener) error { - logger := logging.Logger(s.baseCtx, "nitro.Serve") - if l == nil { - var err error - l, err = vsock.Listen(s.port, &vsock.Config{}) - if err != nil { - return err - } - defer closers.Panic(s.baseCtx, l) - } - logger.Info().Uint32("port", s.port).Msg("Listening to connections on vsock port") - - for { - conn, err := l.Accept() - if err != nil { - logger.Error().Err(err).Msg("accept failed") - } - - go handleLogConn(s.baseCtx, conn) - } -} - -func handleLogConn(ctx context.Context, conn net.Conn) { - logger := logging.Logger(ctx, "nitro.handleLogConn") - - logger.Debug().Msg("Accepted connection.") - defer closers.Panic(ctx, conn) - defer logger.Debug().Msg("Closed connection.") - - for { - buf := make([]byte, 1024) - size, err := conn.Read(buf) - if err != nil { - return - } - if _, err := os.Stdout.Write(buf[:size]); err != nil { - logger.Error().Err(err).Msg("failed to write") - } - } -} diff --git a/libs/nitro/log_test.go b/libs/nitro/log_test.go deleted file mode 100644 index 230622cec..000000000 --- a/libs/nitro/log_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package nitro - -import ( - "context" - "net" - "testing" - "time" - - "github.com/rs/zerolog" -) - -func TestServe(t *testing.T) { - l, err := net.Listen("tcp", "127.0.0.1:1234") - if err != nil { - t.Error("Unexpected error listening") - } - s := NewVsockLogServer(context.Background(), 1234) - go func() { - if err := s.Serve(l); err != nil { - t.Error("failed to serve log server") - } - }() - - log := zerolog.New(NewVsockWriter("127.0.0.1:1234")) - log.Info().Msg("hello world") - time.Sleep(1000 * time.Millisecond) - - log = zerolog.New(zerolog.ConsoleWriter{Out: NewVsockWriter("127.0.0.1:1234")}) - log.Info().Msg("hello world") - time.Sleep(1000 * time.Millisecond) -} diff --git a/libs/nitro/pkcs7/decrypt.go b/libs/nitro/pkcs7/decrypt.go deleted file mode 100644 index 9e89092c0..000000000 --- a/libs/nitro/pkcs7/decrypt.go +++ /dev/null @@ -1,194 +0,0 @@ -package pkcs7 - -/* -https://github.com/mozilla-services/pkcs7/blob/master/decrypt.go - -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import ( - "bytes" - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "encoding/asn1" - "errors" - "fmt" -) - -// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed -var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") - -// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data -var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") - -// Decrypt decrypts encrypted content info for recipient cert and private key -func (p7 *PKCS7) Decrypt(pkey crypto.PrivateKey) ([]byte, error) { - data, ok := p7.raw.(envelopedData) - if !ok { - return nil, ErrNotEncryptedContent - } - if len(data.RecipientInfos) < 1 { - return nil, errors.New("pkcs7: no enveloped recipient") - } - recipient := data.RecipientInfos[0] - switch pkey := pkey.(type) { - case *rsa.PrivateKey: - var contentKey []byte - contentKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pkey, recipient.EncryptedKey, nil) - if err != nil { - return nil, err - } - return data.EncryptedContentInfo.decrypt(contentKey) - } - return nil, ErrUnsupportedAlgorithm -} - -// DecryptUsingPSK decrypts encrypted data using caller provided -// pre-shared secret -func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) { - data, ok := p7.raw.(encryptedData) - if !ok { - return nil, ErrNotEncryptedContent - } - return data.EncryptedContentInfo.decrypt(key) -} - -func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { - alg := eci.ContentEncryptionAlgorithm.Algorithm - if !alg.Equal(OIDEncryptionAlgorithmDESCBC) && - !alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES256CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES128CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES128GCM) && - !alg.Equal(OIDEncryptionAlgorithmAES256GCM) { - fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) - return nil, ErrUnsupportedAlgorithm - } - - // EncryptedContent can either be constructed of multple OCTET STRINGs - // or _be_ a tagged OCTET STRING - var cyphertext []byte - if eci.EncryptedContent.IsCompound { - // Complex case to concat all of the children OCTET STRINGs - var buf bytes.Buffer - cypherbytes := eci.EncryptedContent.Bytes - for { - var part []byte - cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) - buf.Write(part) - if cypherbytes == nil { - break - } - } - cyphertext = buf.Bytes() - } else { - // Simple case, the bytes _are_ the cyphertext - cyphertext = eci.EncryptedContent.Bytes - } - - var block cipher.Block - var err error - - switch { - case alg.Equal(OIDEncryptionAlgorithmDESCBC): - block, err = des.NewCipher(key) - case alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC): - block, err = des.NewTripleDESCipher(key) - case alg.Equal(OIDEncryptionAlgorithmAES256CBC), alg.Equal(OIDEncryptionAlgorithmAES256GCM): - fallthrough - case alg.Equal(OIDEncryptionAlgorithmAES128GCM), alg.Equal(OIDEncryptionAlgorithmAES128CBC): - block, err = aes.NewCipher(key) - } - - if err != nil { - return nil, err - } - - if alg.Equal(OIDEncryptionAlgorithmAES128GCM) || alg.Equal(OIDEncryptionAlgorithmAES256GCM) { - params := aesGCMParameters{} - paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes - - _, err := asn1.Unmarshal(paramBytes, ¶ms) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - if len(params.Nonce) != gcm.NonceSize() { - return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") - } - if params.ICVLen != gcm.Overhead() { - return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") - } - - plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil - } - - iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes - if len(iv) != block.BlockSize() { - return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") - } - mode := cipher.NewCBCDecrypter(block, iv) - plaintext := make([]byte, len(cyphertext)) - mode.CryptBlocks(plaintext, cyphertext) - if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { - return nil, err - } - return plaintext, nil -} - -func unpad(data []byte, blocklen int) ([]byte, error) { - if blocklen < 1 { - return nil, fmt.Errorf("invalid blocklen %d", blocklen) - } - if len(data)%blocklen != 0 || len(data) == 0 { - return nil, fmt.Errorf("invalid data len %d", len(data)) - } - - // the last byte is the length of padding - padlen := int(data[len(data)-1]) - - // check padding integrity, all bytes should be the same - pad := data[len(data)-padlen:] - for _, padbyte := range pad { - if padbyte != byte(padlen) { - return nil, errors.New("invalid padding") - } - } - - return data[:len(data)-padlen], nil -} diff --git a/libs/nitro/pkcs7/encrypt.go b/libs/nitro/pkcs7/encrypt.go deleted file mode 100644 index 9a2bd5026..000000000 --- a/libs/nitro/pkcs7/encrypt.go +++ /dev/null @@ -1,94 +0,0 @@ -package pkcs7 - -/* -https://github.com/mozilla-services/pkcs7/blob/master/encrypt.go - -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import ( - "crypto/x509/pkix" - "encoding/asn1" - "errors" -) - -type envelopedData struct { - Version int - RecipientInfos []recipientInfo `asn1:"set"` - EncryptedContentInfo encryptedContentInfo -} - -type encryptedData struct { - Version int - EncryptedContentInfo encryptedContentInfo -} - -type recipientInfo struct { - Version int - RecipientIdentifier []byte `asn1:"tag:0"` - KeyEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedKey []byte -} - -type encryptedContentInfo struct { - ContentType asn1.ObjectIdentifier - ContentEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedContent asn1.RawValue `asn1:"tag:0,optional"` -} - -const ( - // EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm - EncryptionAlgorithmDESCBC = iota - - // EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm - // Avoid this algorithm unless required for interoperability; use AES GCM instead. - EncryptionAlgorithmAES128CBC - - // EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm - // Avoid this algorithm unless required for interoperability; use AES GCM instead. - EncryptionAlgorithmAES256CBC - - // EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm - EncryptionAlgorithmAES128GCM - - // EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm - EncryptionAlgorithmAES256GCM -) - -// ContentEncryptionAlgorithm determines the algorithm used to encrypt the -// plaintext message. Change the value of this variable to change which -// algorithm is used in the Encrypt() function. -var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC - -// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt -// content with an unsupported algorithm. -var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported") - -// ErrPSKNotProvided is returned when attempting to encrypt -// using a PSK without actually providing the PSK. -var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided") - -type aesGCMParameters struct { - Nonce []byte `asn1:"tag:4"` - ICVLen int -} diff --git a/libs/nitro/pkcs7/pkcs7.go b/libs/nitro/pkcs7/pkcs7.go deleted file mode 100644 index 5218fefbb..000000000 --- a/libs/nitro/pkcs7/pkcs7.go +++ /dev/null @@ -1,105 +0,0 @@ -// Package pkcs7 implements parsing and generation of some PKCS#7 structures. -package pkcs7 - -/* -https://github.com/mozilla-services/pkcs7/blob/master/pkcs7.go - -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import ( - "encoding/asn1" - "errors" - - _ "crypto/sha1" // for crypto.SHA1 -) - -// PKCS7 Represents a PKCS7 structure -type PKCS7 struct { - raw interface{} -} - -type contentInfo struct { - ContentType asn1.ObjectIdentifier - Content envelopedData `asn1:"explicit,optional,tag:0"` -} - -// ErrUnsupportedContentType is returned when a PKCS7 content is not supported. -// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), -// and Enveloped Data are supported (1.2.840.113549.1.7.3) -var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") - -var ( - // Signed Data OIDs - OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} - OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} - OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} - OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} - OIDAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} - OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} - OIDAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} - - // Digest Algorithms - OIDDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} - OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} - OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} - OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} - - OIDDigestAlgorithmDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} - OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - - OIDDigestAlgorithmECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} - - // Signature Algorithms - OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} - OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - - OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} - OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} - OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} - - // Encryption Algorithms - OIDEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} - OIDEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} - OIDEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} - OIDEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} - OIDEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} - OIDEncryptionAlgorithmAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} -) - -// Parse the der bytes of the pkcs7 structure -func Parse(der []byte) (*PKCS7, error) { - var ci contentInfo - if _, err := asn1.Unmarshal(der, &ci); err != nil { - return nil, err - } - return &PKCS7{ - raw: ci.Content, - }, nil -} diff --git a/libs/nitro/vsock.go b/libs/nitro/vsock.go deleted file mode 100644 index 90880eb71..000000000 --- a/libs/nitro/vsock.go +++ /dev/null @@ -1,242 +0,0 @@ -package nitro - -import ( - "context" - "fmt" - "io" - "net" - "net/http" - "net/http/httputil" - "net/url" - "regexp" - "strconv" - "strings" - "sync" - "time" - - "github.com/brave-intl/bat-go/libs/closers" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/mdlayher/vsock" -) - -// NotVsockAddrError indicates that the string does not have the correct structure for a vsock address -type NotVsockAddrError struct{} - -func (NotVsockAddrError) Error() string { - return "addr is not a vsock address" -} - -var vsockAddrRegex = regexp.MustCompile(`vm\((\d)\)`) - -func parseVsockAddr(addr string) (uint32, uint32, error) { - parts := strings.Split(addr, ":") - if len(parts) != 1 && len(parts) != 2 { - return 0, 0, NotVsockAddrError{} - } - - // default to port 80 if none is specified - port := uint64(80) - matches := vsockAddrRegex.FindStringSubmatch(parts[0]) - if len(matches) < 2 { - return 0, 0, NotVsockAddrError{} - } - cid, err := strconv.ParseUint(matches[1], 10, 32) - if err != nil { - return 0, 0, fmt.Errorf("cid must be a valid uint32: %v", err) - } - if len(parts) == 2 { - port, err = strconv.ParseUint(parts[1], 10, 32) - if err != nil { - return 0, 0, fmt.Errorf("port must be a valid uint32: %v", err) - } - } - - return uint32(cid), uint32(port), nil -} - -// DialContext is a net.Dial wrapper which additionally allows connecting to vsock networks -func DialContext(ctx context.Context, network, addr string) (net.Conn, error) { - logger := logging.Logger(ctx, "nitro.DialContext") - logger.Debug(). - Str("network", fmt.Sprintf("%v", network)). - Str("addr", fmt.Sprintf("%v", addr)). - Msg("DialContext") - - cid, port, err := parseVsockAddr(addr) - if err != nil { - if _, ok := err.(NotVsockAddrError); ok { - // fallback to net.Dial - return net.Dial(network, addr) - } - logger.Error().Err(err). - Str("cid", fmt.Sprintf("%v", cid)). - Str("port", fmt.Sprintf("%v", port)). - Msg("error in vsock dial") - return nil, err - } - - logger.Debug(). - Str("cid", fmt.Sprintf("%v", cid)). - Str("port", fmt.Sprintf("%v", port)). - Msg("vsock dialing now") - return vsock.Dial(cid, port, &vsock.Config{}) -} - -type proxyClientConfig struct { - Ctx context.Context - Addr string -} - -func (p *proxyClientConfig) Proxy(*http.Request) (*url.URL, error) { - logger := logging.Logger(p.Ctx, "nitro.Proxy") - logger.Debug(). - Str("addr", p.Addr). - Msg("performing proxy") - v, err := url.Parse(p.Addr) - if err != nil { - logger.Error().Err(err). - Str("addr", p.Addr). - Msg("error parsing address") - } - - return v, err -} - -// NewProxyRoundTripper returns an http.RoundTripper which routes outgoing requests through the proxy addr -func NewProxyRoundTripper(ctx context.Context, addr string) http.RoundTripper { - config := proxyClientConfig{ctx, addr} - return &http.Transport{ - Proxy: config.Proxy, - DialContext: DialContext, - } -} - -// NewReverseProxyServer returns an HTTP server acting as a reverse proxy for the upstream addr specified -func NewReverseProxyServer( - addr string, - upstreamURL string, -) (*http.Server, error) { - proxyURL, err := url.Parse(upstreamURL) - if err != nil { - return nil, fmt.Errorf("Could not parse upstreamURL: %v", err) - } - proxy := httputil.NewSingleHostReverseProxy(proxyURL) - proxy.Transport = &http.Transport{ - DialContext: DialContext, - } - proxy.Director = func(req *http.Request) { - req.Header.Add("X-Forwarded-Host", req.Host) - req.Header.Add("X-Origin-Host", proxyURL.Host) - req.URL.Scheme = proxyURL.Scheme - req.URL.Host = proxyURL.Host - } - - return &http.Server{ - Addr: addr, - Handler: proxy, - }, nil -} - -type openProxy struct { - ConnectTimeout time.Duration -} - -// ServeOpenProxy creates a new open HTTP proxy listening on the specified vsock port -func ServeOpenProxy( - ctx context.Context, - port uint32, - connectTimeout time.Duration, -) error { - - logger := logging.Logger(ctx, "nitro") - logger.Info().Msg("!!!! starting open proxy") - - server := &http.Server{ - Addr: fmt.Sprintf(":%d", port), - Handler: openProxy{ConnectTimeout: connectTimeout}, - } - - l, err := vsock.Listen(port, &vsock.Config{}) - if err != nil { - logger.Error().Err(err).Msg(fmt.Sprintf("listening on vsock port: %v", port)) - return fmt.Errorf("listening on vsock port failed: %v", err) - } - defer closers.Panic(ctx, l) - - logger.Info().Msg(fmt.Sprintf("listening on vsock port: %v", port)) - - return server.Serve(l) -} - -func (op openProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodConnect { - op.httpProxyHandler(w, r) - } else { - op.httpConnectProxyHandler(w, r) - } -} - -func (op openProxy) httpProxyHandler(w http.ResponseWriter, r *http.Request) { - resp, err := http.DefaultTransport.RoundTrip(r) - if err != nil { - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return - } - defer closers.Panic(r.Context(), resp.Body) - for key, values := range resp.Header { - for _, value := range values { - w.Header().Add(key, value) - } - } - w.WriteHeader(resp.StatusCode) - _, _ = io.Copy(w, resp.Body) -} - -func (op openProxy) httpConnectProxyHandler(w http.ResponseWriter, r *http.Request) { - upstream, err := net.DialTimeout("tcp", r.Host, op.ConnectTimeout) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - http.Error(w, "upstream connect timed out", http.StatusGatewayTimeout) - return - } - - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - w.WriteHeader(http.StatusOK) - - hj, ok := w.(http.Hijacker) - if !ok { - http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError) - return - } - conn, _, err := hj.Hijack() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - go bidirectionalCopy(r.Context(), conn, upstream) -} - -func bidirectionalCopy(ctx context.Context, a net.Conn, b net.Conn) { - defer closers.Panic(ctx, a) - defer closers.Panic(ctx, b) - - var wg sync.WaitGroup - // Per https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.6 - // A tunnel is closed when a tunnel intermediary detects that either - // side has closed its connection: the intermediary MUST attempt to send - // any outstanding data that came from the closed side to the other - // side, close both connections, and then discard any remaining data - // left undelivered. - wg.Add(1) - go syncCopy(&wg, b, a) - wg.Add(1) - go syncCopy(&wg, a, b) - wg.Wait() -} - -func syncCopy(wg *sync.WaitGroup, dst io.WriteCloser, src io.ReadCloser) { - defer wg.Done() - _, _ = io.Copy(dst, src) -} diff --git a/libs/passphrase/passphrase.go b/libs/passphrase/passphrase.go deleted file mode 100644 index df3318346..000000000 --- a/libs/passphrase/passphrase.go +++ /dev/null @@ -1,72 +0,0 @@ -// Package passphrase implements passphrase based signing key derivation / backup from github.com/brave/crypto. -package passphrase - -import ( - "crypto/sha512" - "encoding/hex" - "fmt" - "io" - "strings" - - "github.com/superp00t/niceware" - bip39 "github.com/tyler-smith/go-bip39" - "golang.org/x/crypto/ed25519" - "golang.org/x/crypto/hkdf" -) - -var ( - // LedgerHKDFSalt from browser for deriving anonymous wallet signing key. - // NOTE do not reuse this for other purposes, generate a new salt for new uses. - LedgerHKDFSalt = []byte{126, 244, 99, 158, 51, 68, 253, 80, 133, 183, 51, 180, 77, 62, 74, 252, 62, 106, 96, 125, 241, 110, 134, 87, 190, 208, 158, 84, 125, 69, 246, 207, 162, 247, 107, 172, 37, 34, 53, 246, 105, 20, 215, 5, 248, 154, 179, 191, 46, 17, 6, 72, 210, 91, 10, 169, 145, 248, 22, 147, 117, 24, 105, 12} -) - -// DeriveSigningKeysFromSeed using optional salt. -func DeriveSigningKeysFromSeed(seed, salt []byte) (ed25519.PrivateKey, error) { - // NOTE info as []byte{0} not nil - hkdf := hkdf.New(sha512.New, seed, salt, []byte{0}) - - key := make([]byte, ed25519.SeedSize) - _, err := io.ReadFull(hkdf, key) - if err != nil { - return key, err - } - - return ed25519.NewKeyFromSeed(key), nil -} - -// FromBytes converts bytes to passphrase using bip39. -func FromBytes(in []byte) ([]string, error) { - phrase, err := bip39.NewMnemonic(in) - return strings.Fields(phrase), err -} - -// FromHex converts hex bytes to passphrase using bip39. -func FromHex(in string) ([]string, error) { - b, err := hex.DecodeString(in) - if err != nil { - return nil, err - } - return FromBytes(b) -} - -// ToBytes32 converts a 32-byte passphrase to bytes. -// Infers whether the passphrase is bip39 or niceware based on length. -func ToBytes32(phrase string) ([]byte, error) { - words := strings.Fields(phrase) - if len(words) == 16 { - return niceware.PassphraseToBytes(words) - } else if len(words) == 24 { - return bip39.EntropyFromMnemonic(phrase) - } - return nil, fmt.Errorf("input words length %d is not 24 or 16", len(words)) -} - -// ToHex32 converts a 32-byte passphrase to hex. -// Infers whether the passphrase is bip39 or niceware based on length. -func ToHex32(phrase string) (string, error) { - bytes, err := ToBytes32(phrase) - if err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} diff --git a/libs/passphrase/passphrase_test.go b/libs/passphrase/passphrase_test.go deleted file mode 100644 index 22c421cb6..000000000 --- a/libs/passphrase/passphrase_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package passphrase - -import ( - "crypto" - "crypto/rand" - "encoding/hex" - "reflect" - "strings" - "testing" - - "golang.org/x/crypto/ed25519" -) - -var array16 []byte -var array32 []byte - -func init() { - array16 = make([]byte, 16) - array32 = make([]byte, 32) - for i := 0; i < 32; i++ { - if i < 16 { - array16[i] = 255 - } - array32[i] = 255 - } -} - -func TestDeriveSigningKeysFromSeed(t *testing.T) { - hkdfSalt := []byte{72, 203, 156, 43, 64, 229, 225, 127, 214, 158, 50, 29, 130, 186, 182, 207, 6, 108, 47, 254, 245, 71, 198, 109, 44, 108, 32, 193, 221, 126, 119, 143, 112, 113, 87, 184, 239, 231, 230, 234, 28, 135, 54, 42, 9, 243, 39, 30, 179, 147, 194, 211, 212, 239, 225, 52, 192, 219, 145, 40, 95, 19, 142, 98} - seed, err := hex.DecodeString("5bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb") - - if err != nil { - t.Error("Unexpected error decoding hex") - } - - key, err := DeriveSigningKeysFromSeed(seed, hkdfSalt) - if err != nil { - t.Error("Unexpected error deriving keys") - } - - if hex.EncodeToString(key) != "b5abda6940984c5153a2ba3653f047f98dfb19e39c3e02f07c8bbb0bd8e8872ef58ca446f0c33ee7e8e9874466da442b2e764afd77ad46034bdff9e01f9b87d4" { - t.Error("Wrong private key!") - } - - pk := key.Public().(ed25519.PublicKey) - if hex.EncodeToString(pk) != "f58ca446f0c33ee7e8e9874466da442b2e764afd77ad46034bdff9e01f9b87d4" { - t.Error("Wrong public key!") - } - - message := []byte("€ 123 ッッッ あ") - sig, err := key.Sign(rand.Reader, message, crypto.Hash(0)) - if err != nil { - t.Error("Unexpected error signing message") - } - - // nacl combined signature mode is equivalent to the message with signature prepended - if hex.EncodeToString(sig)+hex.EncodeToString(message) != "81d39235e6e1ed07e7d2cee6380aa07f415ab4b43cc4cef4043e61171ac5511253807105aba9b7b9c9cdbb0e84517178176d87f757801275a16477a51013e10de282ac2031323320e38383e38383e38383e38080e38182" { - t.Error("Signature did not match expected value from brave/crypto") - } - - if !ed25519.Verify(pk, message, sig) { - t.Error("Signature verification failed") - } - - sig[0] = 255 - if ed25519.Verify(pk, message, sig) { - t.Error("Signature verification should have failed") - } -} - -func TestFromHex(t *testing.T) { - phrase, err := FromHex("00000000000000000000000000000000") - if err != nil { - t.Error("Error during hex to bip39 phrase") - } - if strings.Join(phrase, " ") != "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" { - t.Error("bip39 phrase did not match") - } -} - -func TestFromBytes(t *testing.T) { - phrase, err := FromBytes(array16) - if err != nil { - t.Error("Error during hex to bip39 phrase") - } - if strings.Join(phrase, " ") != "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" { - t.Error("bip39 phrase did not match") - } - - phrase, err = FromBytes(array32) - if err != nil { - t.Error("Error during hex to bip39 phrase") - } - if strings.Join(phrase, " ") != "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote" { - t.Error("bip39 phrase did not match") - } -} - -func TestToBytes32(t *testing.T) { - result, err := ToBytes32("a a a a a a a a a a a a a a a a") - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if !reflect.DeepEqual(result, make([]byte, 32)) { - t.Error("Resulting bytes did not match expectation") - } - - result, err = ToBytes32(" zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva") - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if !reflect.DeepEqual(result, array32) { - t.Error("Resulting bytes did not match expectation") - } - - result, err = ToBytes32("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote") - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if !reflect.DeepEqual(result, array32) { - t.Error("Resulting bytes did not match expectation") - } - - _, err = ToBytes32("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong") - if err == nil { - t.Error("Expected error due to incorrect phrase length") - } -} - -func TestToHex32(t *testing.T) { - result, err := ToHex32("horsepox tiglon monolithic impoundment classiest propagation deviant temporize precessed sunburning pricey spied plack batcher overpassed bioengineering") - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if result != "65f9e2ea89dd6a8d2333ab0b3808e011a757da60a95cd201a2e40df098f111d4" { - t.Error("Resulting hex did not match expectation") - } - - result, err = ToHex32(" zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva zyzzyva") - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if result != "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" { - t.Error("Resulting hex did not match expectation") - } - - result, err = ToHex32("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote") - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if result != "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" { - t.Error("Resulting hex did not match expectation") - } - - _, err = ToHex32("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") - if err == nil { - t.Error("Expected error due to incorrect phrase length") - } -} - -func TestOriginalSeedCanBeRecovered(t *testing.T) { - hex := "65f9e2ea89dd6a8d2333ab0b3808e011a757da60a95cd201a2e40df098f111d4" - phrase, err := FromHex(hex) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - oHex, err := ToHex32(strings.Join(phrase, " ")) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if oHex != hex { - t.Error("Resulting hex did not match original") - } - - b := make([]byte, 32) - _, err = rand.Read(b) - if err != nil { - t.Error("Unexpected error on random read") - } - - phrase, err = FromBytes(b) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - ob, err := ToBytes32(strings.Join(phrase, " ")) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if !reflect.DeepEqual(ob, b) { - t.Error("Resulting bytes did not match original") - } -} - -func TestOriginalPhraseCanBeRecovered(t *testing.T) { - phrase := "magic vacuum wide review love peace century egg burden clutch heart cycle annual mixed pink awesome extra client cry brisk priority maple mountain jelly" - - hex, err := ToHex32(phrase) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - oPhrase, err := FromHex(hex) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if phrase != strings.Join(oPhrase, " ") { - t.Error("Resulting phrase did not match original") - } - - b, err := ToBytes32(phrase) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - oPhrase, err = FromBytes(b) - if err != nil { - t.Error("Unexpected error on valid phrase") - } - if phrase != strings.Join(oPhrase, " ") { - t.Error("Resulting phrase did not match original") - } -} diff --git a/libs/payments/api.go b/libs/payments/api.go deleted file mode 100644 index 018cef87f..000000000 --- a/libs/payments/api.go +++ /dev/null @@ -1,10 +0,0 @@ -package payments - -var ( - APIBase = map[string]string{ - "": "https://nitro-payments.bsg.brave.software", - "local": "https://nitro-payments.bsg.brave.software", - "development": "https://nitro-payments.bsg.brave.software", - "staging": "https://nitro-payments-staging.bsg.brave.com", - } -) diff --git a/libs/payments/crypto.go b/libs/payments/crypto.go deleted file mode 100644 index 3f0077d7c..000000000 --- a/libs/payments/crypto.go +++ /dev/null @@ -1,25 +0,0 @@ -package payments - -import ( - "context" - "crypto" - "io" - "time" -) - -// Signator is an interface for cryptographic signature creation -// NOTE that this is a subset of the crypto.Signer interface -type Signator interface { - Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) -} - -// Verifier is an interface for verifying signatures -type Verifier interface { - Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) -} - -// Keystore provides a way to lookup a public key based on the keyID a request was signed with -type Keystore interface { - // LookupVerifier based on the keyID and updatedAt - LookupVerifier(ctx context.Context, keyID string, updatedAt time.Time) (context.Context, *Verifier, error) -} diff --git a/libs/payments/errors.go b/libs/payments/errors.go deleted file mode 100644 index 9ae665d6f..000000000 --- a/libs/payments/errors.go +++ /dev/null @@ -1,51 +0,0 @@ -package payments - -import ( - "fmt" -) - -// PaymentError is an error used to communicate whether an error is temporary. -type PaymentError struct { - originalError error `json:"-"` - Message string `json:"message"` - Temporary bool `json:"temporary"` -} - -// Error makes ProcessingError an error -func (e PaymentError) Error() string { - msg := fmt.Sprintf("error: %s", e.originalError) - if e.Cause() != nil { - msg = fmt.Sprintf("%s: %s", msg, e.Cause()) - } - return msg -} - -// Cause implements Cause for error -func (e PaymentError) Cause() error { - return e.originalError -} - -// Unwrap implements Unwrap for error -func (e PaymentError) Unwrap() error { - return e.originalError -} - -// ProcessingErrorFromError - given an error turn it into a processing error -func ProcessingErrorFromError(cause error, isTemporary bool) *PaymentError { - return &PaymentError{ - originalError: cause, - Message: cause.Error(), - Temporary: isTemporary, - } -} - -// InvalidTransitionState indicates that the payment state transition is invalid -type InvalidTransitionState struct { - From string - To string -} - -// Error makes InvalidTransitionState an error -func (e *InvalidTransitionState) Error() string { - return fmt.Sprintf("invalid state transition from %s to %s.", e.From, e.To) -} diff --git a/libs/payments/messages.go b/libs/payments/messages.go deleted file mode 100644 index 63a162779..000000000 --- a/libs/payments/messages.go +++ /dev/null @@ -1,60 +0,0 @@ -package payments - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/google/uuid" -) - -// WorkerConfig defines the settlement worker configuration structure -type WorkerConfig struct { - PayoutID string `json:"payoutId"` - ConsumerGroup string `json:"consumerGroup"` - Stream string `json:"stream"` - Count int `json:"count"` - BatchSize int64 `json:"batchSize"` -} - -// MarshalBinary implements encoding.BinaryMarshaler required for go-redis -func (wc WorkerConfig) MarshalBinary() (data []byte, err error) { - bytes, err := json.Marshal(wc) - if err != nil { - return nil, fmt.Errorf("event message: error marshalling binary: %w", err) - } - return bytes, nil -} - -// RequestWrapper defines the settlement worker submit message structure -type RequestWrapper struct { - ID uuid.UUID `json:"id"` - Timestamp time.Time `json:"timestamp"` - Request *httpsignature.HTTPSignedRequest `json:"request"` -} - -// MarshalBinary implements encoding.BinaryMarshaler required for go-redis -func (sw RequestWrapper) MarshalBinary() (data []byte, err error) { - bytes, err := json.Marshal(sw) - if err != nil { - return nil, fmt.Errorf("event message: error marshalling binary: %w", err) - } - return bytes, nil -} - -// ResponseWrapper defines the settlement worker submit message structure -type ResponseWrapper struct { - ID uuid.UUID `json:"id"` - Timestamp time.Time `json:"timestamp"` - Response *httpsignature.HTTPSignedResponse `json:"response"` -} - -// MarshalBinary implements encoding.BinaryMarshaler required for go-redis -func (sw ResponseWrapper) MarshalBinary() (data []byte, err error) { - bytes, err := json.Marshal(sw) - if err != nil { - return nil, fmt.Errorf("event message: error marshalling binary: %w", err) - } - return bytes, nil -} diff --git a/libs/payments/requests.go b/libs/payments/requests.go deleted file mode 100644 index 366fffdc8..000000000 --- a/libs/payments/requests.go +++ /dev/null @@ -1,84 +0,0 @@ -package payments - -// PrepareRequest is provided for the initial creation and preparation of a payment. This payment -// must be unique in the database by idempotencyKey, which is derived from the included -// PaymentDetails. -type PrepareRequest struct { - PaymentDetails -} - -// PrepareResponse is sent to the client in response to a PrepareRequest. -type PrepareResponse struct { - PaymentDetails - DocumentID string `json:"documentId,omitempty"` -} - -// SubmitRequest is provided to indicate a payment that should be executed. -type SubmitRequest struct { - DocumentID string `json:"documentId,omitempty"` - PayoutID string `json:"payoutId" valid:"required"` -} - -// SubmitResponse is returned to provide the status of a payment after submission, along with any -// error that resulted, if necessary. -type SubmitResponse struct { - Status PaymentStatus `json:"status" valid:"required"` - PaymentDetails `json:"paymentDetails,omitempty"` - ExternalIdempotency string `json:"externalIdempotency,omitempty"` -} - -// AddressApprovalRequest is provided to indicate approval of an on-chain address. -type AddressApprovalRequest struct { - Address string `json:"address" valid:"required"` -} - -// OperatorShare represents the association between an operator name and their encrypted share -type OperatorShareData struct { - Name string `json:"name" valid:"required"` - Material []byte `json:"material" valid:"required"` -} - -type OperatorPubkeyData struct { - Name string `json:"name" valid:"required"` - PublicKey string `json:"publicKey" valid:"required"` -} - -// CreateVaultRequest is provided to request vault creation for secrets storage. -type CreateVaultRequest struct { - Threshold int `json:"threshold" valid:"required"` -} - -// CreateVaultResponse provides shares, associated with names provided in CreateVaultRequest, as -// well as the public key resulting from creation and the threshold specified in the request. -type CreateVaultResponse struct { - Shares []OperatorShareData `json:"operatorData" valid:"required"` - PublicKey string `json:"publicKey" valid:"required"` - Threshold int `json:"threshold" valid:"required"` -} - -// CreateVaultResponseWrapper is a data wrapper that exposes the service's response object to the -// client -type CreateVaultResponseWrapper struct { - Data CreateVaultResponse `json:"data"` -} - -// VerifyVaultResponseWrapper is a data wrapper that exposes the service's response object to the -// client -type VerifyVaultResponseWrapper struct { - Data VerifyVaultResponse `json:"data"` -} - -// VerifyVaultRequest is provided to request vault approval for a given configuration and public -// key. The provided parameters and public key must exist and match in QLDB for approval to succeed. -type VerifyVaultRequest struct { - Threshold int `json:"threshold" valid:"required"` - PublicKey string `json:"publicKey" valid:"required"` -} - -// VerifyVaultResponse returns the number of approvals, whether a vault is fully approved, and the -// public key of the approved vault. -type VerifyVaultResponse struct { - Operators []string `json:"operatorKeys" valid:"required"` - Threshold int `json:"threshold" valid:"required"` - PublicKey string `json:"publicKey" valid:"required"` -} diff --git a/libs/payments/state.go b/libs/payments/state.go deleted file mode 100644 index adb1e8702..000000000 --- a/libs/payments/state.go +++ /dev/null @@ -1,192 +0,0 @@ -package payments - -import ( - "context" - "crypto" - "crypto/rand" - "encoding/json" - "fmt" - "time" - - "github.com/google/uuid" - "github.com/shopspring/decimal" -) - -// PaymentDetails captures the key details of a particular payment to be executed as part of a settlement. -// This is the minimum information needed to execute a transfer. -type PaymentDetails struct { - Amount decimal.Decimal `json:"amount" valid:"required"` - To string `json:"to" valid:"required"` - From string `json:"from" valid:"required"` - Custodian string `json:"custodian" valid:"in(uphold|gemini|bitflyer|zebpay|solana)"` - PayoutID string `json:"payoutId" valid:"required"` - Currency string `json:"currency" valid:"required"` -} - -// PaymentAuthorization represents a single authorization from a payment authorizer indicating that -// the payout represented by a document ID should be processed -type PaymentAuthorization struct { - KeyID string `json:"keyId" valid:"required"` - DocumentID string `json:"documentId" valid:"required"` -} - -// AuthenticatedPaymentState is a payment state whose providence has been authenticated as originating -// within an enclave. -type AuthenticatedPaymentState struct { - PaymentDetails - Status PaymentStatus `json:"status"` - Authorizations []PaymentAuthorization `json:"authorizations"` - LastError *PaymentError `json:"lastError"` - DocumentID string `json:"documentID"` - // ExternalIdempotency is for state machines which have third party idempotency values that are - // not derministically generated from data that we control but that need to be retained between - // calls to Pay(). For example, Solana requires the block hash and transaction signature to - // guarantee idempotency. - ExternalIdempotency []byte `json:"externalIdempotency"` -} - -// PaymentState is the high level structure which is stored in a datastore. -// It includes the full payment state as well as authentication information which proves it is a valid -// object authored by the enclave. Accessing the payment state directly is considered unsafe, one must -// go through a getter which verifies the history. -type PaymentState struct { - // Serialized AuthenticatedPaymentState. Should only ever be access via GetAuthenticatedPaymentState, - // which does all of the needed validation of the state - UnsafePaymentState []byte `ion:"data"` - Signature []byte `ion:"signature"` - PublicKey string `ion:"publicKey"` - ID uuid.UUID `ion:"idempotencyKey"` - UpdatedAt time.Time `ion:"-"` -} - -// PaymentStateHistory is a sequence of payment states. -type PaymentStateHistory []PaymentState - -// IdempotencyKey calculates the idempotency key for these PaymentDetails -func (p PaymentDetails) IdempotencyKey() uuid.UUID { - return uuid.NewSHA1( - uuid.MustParse("3c0e75eb-9150-40b4-a988-a017d115de3c"), - []byte(fmt.Sprintf( - "%s%s%s%s%s%s", - p.To, - p.From, - p.Currency, - p.Amount, - p.Custodian, - p.PayoutID, - )), - ) -} - -// ToAuthenticatedPaymentState creates an new AuthenticatedPaymentState from PaymentDetails -func (p PaymentDetails) ToAuthenticatedPaymentState() *AuthenticatedPaymentState { - authenticatedState := AuthenticatedPaymentState{ - PaymentDetails: p, - } - return &authenticatedState -} - -// ToPaymentState creates an unsigned PaymentState from an AuthenticatedPaymentState -func (t AuthenticatedPaymentState) ToPaymentState() (*PaymentState, error) { - marshaledState, err := json.Marshal(t) - if err != nil { - return nil, err - } - - paymentState := PaymentState{ - UnsafePaymentState: marshaledState, - ID: t.PaymentDetails.IdempotencyKey(), - } - - return &paymentState, nil -} - -// NextStateValid returns true if nextState is a valid transition from the current one -func (t *AuthenticatedPaymentState) NextStateValid(nextState PaymentStatus) bool { - if t.Status == nextState { - return true - } - // New transaction state should be present in the list of valid next states for the current - // state. - return statusListContainsStatus(t.Status.GetValidTransitions(), nextState) -} - -// statusListContainsStatus returns true if the status list contains the passed status -func statusListContainsStatus(s []PaymentStatus, e PaymentStatus) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} - -// Sign this payment state, authenticating the contents of UnsafePaymentState -func (p *PaymentState) Sign(signer Signator, publicKey string) error { - var err error - p.Signature, err = signer.Sign(rand.Reader, p.UnsafePaymentState, crypto.Hash(0)) - if err != nil { - return fmt.Errorf("Failed to sign payment state: %w", err) - } - p.PublicKey = publicKey - return nil -} - -// GetAuthenticatedPaymentState by performing the appropriate validation. -func (p PaymentStateHistory) GetAuthenticatedPaymentState(keystore Keystore, documentID string) (*AuthenticatedPaymentState, error) { - // iterate through our payment states checking: - // 1. the signature - // 2. the documentID matches any internal documentIDs - // 3. the transition was valid - var authenticatedState AuthenticatedPaymentState - for i, state := range []PaymentState(p) { - _, verifier, err := keystore.LookupVerifier(context.Background(), state.PublicKey, state.UpdatedAt) - if err != nil { - return nil, fmt.Errorf("signature validation for state with document ID %s failed: %w", documentID, err) - } - - var unsafeState AuthenticatedPaymentState - valid, err := (*verifier).Verify(state.UnsafePaymentState, state.Signature, crypto.Hash(0)) - if err != nil { - return nil, fmt.Errorf("signature validation for state with document ID %s failed: %w", documentID, err) - } - if !valid { - return nil, fmt.Errorf("signature for state with document ID %s was not valid", documentID) - } - - err = json.Unmarshal(state.UnsafePaymentState, &unsafeState) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal transaction data: %w", err) - } - - if unsafeState.DocumentID != "" && unsafeState.DocumentID != documentID { - return nil, fmt.Errorf("internal document ID %s did not match expected document ID %s", unsafeState.DocumentID, documentID) - } - for _, authorization := range unsafeState.Authorizations { - if authorization.DocumentID != documentID { - return nil, fmt.Errorf("internal authorization document ID %s did not match expected document ID %s", authorization.DocumentID, documentID) - } - } - - if i == 0 { - // must always start in prepared - if unsafeState.Status != Prepared { - return nil, &InvalidTransitionState{} - } - } else { - // New state should be present in the list of valid next states for the - // "previous" (current) state. - if !authenticatedState.NextStateValid(unsafeState.Status) { - return nil, &InvalidTransitionState{ - From: string(authenticatedState.Status), - To: string(unsafeState.Status), - } - } - } - - authenticatedState = unsafeState - } - - authenticatedState.DocumentID = documentID - return &authenticatedState, nil -} diff --git a/libs/payments/state_test.go b/libs/payments/state_test.go deleted file mode 100644 index ad0e2ba43..000000000 --- a/libs/payments/state_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package payments - -import ( - "context" - "crypto" - "encoding/json" - "testing" - "time" - - "github.com/google/uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/assert" -) - -var ( - dID = "HgXBNdOVAy83ZBy5oM26OE" - generatedUUID, _ = uuid.Parse("727ccc14-1951-5a75-bbce-489505a684b1") - amount = decimal.NewFromFloat(1.1) - txn0 = AuthenticatedPaymentState{Status: Prepared, PaymentDetails: PaymentDetails{Amount: amount}} - txn1 = AuthenticatedPaymentState{DocumentID: "HgXBNdOVAy83ZBy5oM26OE", Status: Authorized, PaymentDetails: PaymentDetails{Amount: amount}} - txn2 = AuthenticatedPaymentState{DocumentID: "HgXBNdOVAy83ZBy5oM26OE", Status: Pending, PaymentDetails: PaymentDetails{Amount: amount}} - txn3 = AuthenticatedPaymentState{DocumentID: "HgXBNdOVAy83ZBy5oM26OE", Status: Paid, PaymentDetails: PaymentDetails{Amount: amount}} - txn4 = AuthenticatedPaymentState{DocumentID: "HgXBNdOVAy83ZBy5oM26OE", Status: Failed, PaymentDetails: PaymentDetails{Amount: amount}} - status0, _ = json.Marshal(txn0) - status1, _ = json.Marshal(txn1) - status2, _ = json.Marshal(txn2) - status3, _ = json.Marshal(txn3) - status4, _ = json.Marshal(txn4) -) - -type mockVerifier struct { - value bool -} - -func (m mockVerifier) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) { - return m.value, nil -} - -type mockKeystore struct { - value bool -} - -func (m mockKeystore) LookupVerifier( - ctx context.Context, - keyID string, updatedAt time.Time, -) (context.Context, *Verifier, error) { - var verifier = (Verifier)(mockVerifier{value: m.value}) - return ctx, &verifier, nil -} - -var transactionHistorySetTrue = []PaymentStateHistory{ - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status1, ID: generatedUUID}, - {UnsafePaymentState: status3, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status1, ID: generatedUUID}, - {UnsafePaymentState: status2, ID: generatedUUID}, - {UnsafePaymentState: status3, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status1, ID: generatedUUID}, - {UnsafePaymentState: status2, ID: generatedUUID}, - {UnsafePaymentState: status4, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status1, ID: generatedUUID}, - {UnsafePaymentState: status2, ID: generatedUUID}, - {UnsafePaymentState: status4, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status1, ID: generatedUUID}, - {UnsafePaymentState: status4, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status4, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - }, -} - -var transactionHistorySetFalse = []PaymentStateHistory{ - // Transitions must always start at 0 - { - {UnsafePaymentState: status1, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status2, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status3, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status4, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status3, ID: generatedUUID}, - {UnsafePaymentState: status4, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status3, ID: generatedUUID}, - }, - { - {UnsafePaymentState: status0, ID: generatedUUID}, - {UnsafePaymentState: status2, ID: generatedUUID}, - }, -} - -func TestIdempotencyKey(t *testing.T) { - details := PaymentDetails{ - Amount: decimal.NewFromFloat(12.234), - To: "683bc9ba-497a-47a5-9587-3bd03fd722bd", - From: "af68d02a-907f-4e9a-8f74-b54c7629412b", - Custodian: "uphold", - PayoutID: "78910", - } - assert.Equal( - t, - details.IdempotencyKey().String(), - "29ccbbfd-7a77-5874-a5bb-d043d9f38bf2", - ) -} - -func TestGetAuthenticatedPaymentState(t *testing.T) { - keystore := mockKeystore{value: true} - // Valid transitions should be valid - for _, transactionHistorySet := range transactionHistorySetTrue { - authenticatedState, err := transactionHistorySet.GetAuthenticatedPaymentState( - keystore, - dID, - ) - assert.Nil(t, err) - assert.NotNil(t, authenticatedState) - } - - // Invalid transitions should be invalid - for _, transactionHistorySet := range transactionHistorySetFalse { - authenticatedState, err := transactionHistorySet.GetAuthenticatedPaymentState( - keystore, - dID, - ) - assert.Error(t, err) - assert.Nil(t, authenticatedState) - } - - // Valid transitions should be invalid with wrong doc id - for _, transactionHistorySet := range transactionHistorySetTrue { - authenticatedState, err := transactionHistorySet.GetAuthenticatedPaymentState( - keystore, - "Cn7mea9LNqEH6FWK1XX38o", - ) - // initial state does not have an embedded document id to cause mismatch - if len(transactionHistorySet) > 1 { - assert.Error(t, err) - assert.Nil(t, authenticatedState) - } - } - - keystore.value = false - // Valid transitions should be invalid with bad signatures - for _, transactionHistorySet := range transactionHistorySetTrue { - authenticatedState, err := transactionHistorySet.GetAuthenticatedPaymentState( - keystore, - dID, - ) - assert.Error(t, err) - assert.Nil(t, authenticatedState) - } -} diff --git a/libs/payments/streams.go b/libs/payments/streams.go deleted file mode 100644 index e012cdd06..000000000 --- a/libs/payments/streams.go +++ /dev/null @@ -1,23 +0,0 @@ -package payments - -const ( - // PreparePrefix is the prefix for streams dealing with prepare events - PreparePrefix = "prepare-" - // SubmitPrefix is the prefix for streams dealing with submit events - SubmitPrefix = "submit-" - // ResponseSuffix is the suffix for streams dealing with responses - ResponseSuffix = "-response" - // Statusuffix is the suffix for the set containing response statuses - StatusSuffix = "-status" -) - -var ( - // PrepareConfigStream is the stream for configuration events for new settlement prepare streams - PrepareConfigStream = PreparePrefix + "config" - // PrepareConfigConsumerGroup is the consumergroup for the prepare config stream - PrepareConfigConsumerGroup = PrepareConfigStream + "-wg" - // SubmitConfigStream is the stream for configuration events for new settlement submit streams - SubmitConfigStream = SubmitPrefix + "config" - // SubmitConfigConsumerGroup is the consumergroup for the submit config stream - SubmitConfigConsumerGroup = SubmitConfigStream + "-wg" -) diff --git a/libs/payments/transitions.go b/libs/payments/transitions.go deleted file mode 100644 index bfb51ab5b..000000000 --- a/libs/payments/transitions.go +++ /dev/null @@ -1,63 +0,0 @@ -package payments - -// PaymentStatus is a string representing transaction status. -type PaymentStatus string - -const ( - // empty is an internal only status which is never recorded, it exists purely to transition to prepared. - empty PaymentStatus = "" - // Prepared represents a record that has been prepared for authorization. - Prepared PaymentStatus = "prepared" - // Authorized represents a record that has been authorized. - Authorized PaymentStatus = "authorized" - // Pending represents a record that is being or has been submitted to a processor. - Pending PaymentStatus = "pending" - // Paid represents a record that has entered a finalized success state with a processor. - Paid PaymentStatus = "paid" - // Failed represents a record that has failed processing permanently. - Failed PaymentStatus = "failed" -) - -// Transitions represents the valid forward-transitions for each given state. -var Transitions = map[PaymentStatus][]PaymentStatus{ - empty: {Prepared}, - Prepared: {Authorized, Failed}, - Authorized: {Pending, Paid, Failed}, - Pending: {Paid, Failed}, - Paid: {}, - Failed: {}, -} - -// GetValidTransitions returns valid transitions. -func (ts PaymentStatus) GetValidTransitions() []PaymentStatus { - return Transitions[ts] -} - -// GetAllValidTransitionSequences returns all valid transition sequences. -func GetAllValidTransitionSequences() [][]PaymentStatus { - return RecurseTransitionResolution("prepared", []PaymentStatus{}) -} - -// RecurseTransitionResolution returns the list of valid transition paths that are -// possible for a given state. -func RecurseTransitionResolution( - state PaymentStatus, - currentTree []PaymentStatus, -) [][]PaymentStatus { - var ( - result [][]PaymentStatus - updatedTree = append(currentTree, state) - ) - possibleStates := state.GetValidTransitions() - if len(possibleStates) == 0 { - tempTree := make([]PaymentStatus, len(updatedTree)) - copy(tempTree, updatedTree) - result = append(result, tempTree) - return result - } - for _, possibleState := range possibleStates { - recursed := RecurseTransitionResolution(possibleState, updatedTree) - result = append(result, recursed...) - } - return result -} diff --git a/libs/payments/transitions_test.go b/libs/payments/transitions_test.go deleted file mode 100644 index 8859ca4f5..000000000 --- a/libs/payments/transitions_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package payments - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/assert" -) - -/* -Generate all valid transition sequences and ensure that this test contains the exact same set of -valid transition sequences. The purpose of this test is to alert us if outside changes -impact the set of valid transitions. -*/ -func TestRecurseTransitionResolution(t *testing.T) { - allValidTransitionSequences := RecurseTransitionResolution("prepared", []PaymentStatus{}) - knownValidTransitionSequences := [][]PaymentStatus{ - {Prepared, Authorized, Paid}, - {Prepared, Authorized, Pending, Paid}, - {Prepared, Authorized, Pending, Failed}, - {Prepared, Authorized, Failed}, - {Prepared, Failed}, - } - // Ensure all generatedTransitionSequence have a matching knownValidTransitionSequences - for _, generatedTransitionSequence := range allValidTransitionSequences { - foundMatch := false - for _, knownValidTransitionSequence := range knownValidTransitionSequences { - if reflect.DeepEqual(generatedTransitionSequence, knownValidTransitionSequence) { - foundMatch = true - } - } - assert.True(t, foundMatch) - } - // Ensure all knownValidTransitionSequences have a matching generatedTransitionSequence - for _, knownValidTransitionSequence := range allValidTransitionSequences { - foundMatch := false - for _, generatedTransitionSequence := range allValidTransitionSequences { - if reflect.DeepEqual(generatedTransitionSequence, knownValidTransitionSequence) { - foundMatch = true - } - } - assert.True(t, foundMatch) - } -} diff --git a/libs/pindialer/dialer.go b/libs/pindialer/dialer.go deleted file mode 100644 index b71e37320..000000000 --- a/libs/pindialer/dialer.go +++ /dev/null @@ -1,67 +0,0 @@ -// Package pindialer contains a basic implementation of a pinned HTTPS dialer -package pindialer - -import ( - "context" - "crypto/sha256" - "crypto/tls" - "encoding/base64" - "errors" - "fmt" - "net" -) - -// ContextDialer is a function connecting to the address on the named network -type ContextDialer func(ctx context.Context, network, addr string) (net.Conn, error) - -func validateChain(fingerprint string, connstate tls.ConnectionState) error { - for _, chain := range connstate.VerifiedChains { - for _, cert := range chain { - hash := sha256.Sum256(cert.RawSubjectPublicKeyInfo) - digest := base64.StdEncoding.EncodeToString(hash[:]) - if digest == fingerprint { - return nil - } - } - } - return errors.New("the server certificate was not valid") -} - -// MakeContextDialer returns a ContextDialer that only succeeds on connection to a TLS secured address with the pinned fingerprint -func MakeContextDialer(fingerprint string) ContextDialer { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - c, err := tls.Dial(network, addr, nil) - if err != nil { - return c, err - } - select { - case <-ctx.Done(): - return nil, fmt.Errorf("context completed") - default: - if err := validateChain(fingerprint, c.ConnectionState()); err != nil { - return nil, fmt.Errorf("failed to validate certificate chain: %w", err) - } - } - return c, nil - } -} - -// GetFingerprints is a helper for getting the fingerprint needed to update pins -func GetFingerprints(c *tls.Conn) (map[string]string, error) { - connstate := c.ConnectionState() - - if len(connstate.VerifiedChains) < 1 { - return nil, errors.New("no valid verified chain found") - } - prints := make(map[string]string) - - for _, chain := range connstate.VerifiedChains { - for _, node := range chain { - hash := sha256.Sum256(node.RawSubjectPublicKeyInfo) - digest := base64.StdEncoding.EncodeToString(hash[:]) - prints[node.Issuer.String()] = digest - } - } - - return prints, nil -} diff --git a/libs/prompt/prompt.go b/libs/prompt/prompt.go deleted file mode 100644 index 919796b4f..000000000 --- a/libs/prompt/prompt.go +++ /dev/null @@ -1,38 +0,0 @@ -package prompt - -import ( - "bufio" - "fmt" - "os" - "strings" - "syscall" - - "golang.org/x/term" -) - -// Bool prompts for y/n input returning a bool -func Bool() (bool, error) { - reader := bufio.NewReader(os.Stdin) - for { - fmt.Print("(y/n): ") - var text string - text, err := reader.ReadString('\n') - if err != nil { - return false, err - } - if strings.ToLower(strings.TrimSpace(text)) == "n" { - return false, nil - } else if strings.ToLower(strings.TrimSpace(text)) == "y" { - return true, nil - } else { - fmt.Println("Input must be \"y\" or \"n\"") - } - } -} - -// Password prompts for a password -func Password() ([]byte, error) { - fmt.Print("Enter Password: ") - defer fmt.Print("\n") - return term.ReadPassword(int(syscall.Stdin)) -} diff --git a/libs/ptr/ptr.go b/libs/ptr/ptr.go deleted file mode 100644 index 6c1667b17..000000000 --- a/libs/ptr/ptr.go +++ /dev/null @@ -1,39 +0,0 @@ -package ptr - -import ( - "time" - - uuid "github.com/satori/go.uuid" -) - -// FromUUID returns pointer to uuid -func FromUUID(u uuid.UUID) *uuid.UUID { - return &u -} - -// FromString returns pointer to string -func FromString(s string) *string { - return &s -} - -// String returns value of pointer or empty string -func String(s *string) string { - return StringOr(s, "") -} - -// StringOr returns value of pointer or alternative value -func StringOr(s *string, or string) string { - if s == nil { - return or - } - return *s -} - -// FromTime - get the address of the time -func FromTime(t time.Time) *time.Time { - return &t -} - -func To[T any](v T) *T { - return &v -} diff --git a/libs/redisconsumer/consumer.go b/libs/redisconsumer/consumer.go deleted file mode 100644 index 6e90c9792..000000000 --- a/libs/redisconsumer/consumer.go +++ /dev/null @@ -1,305 +0,0 @@ -package redisconsumer - -import ( - "context" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io/ioutil" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/brave-intl/bat-go/libs/concurrent" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/rootdir" - redis "github.com/redis/go-redis/v9" -) - -// RetryAfterPrefix is the redis key prefix for storing the next advised retry time for a message -const RetryAfterPrefix = "retry-after-" - -// ConsumerSet tracks currently running in-process redis stream consumers to prevent duplicates -var ConsumerSet *concurrent.Set - -func init() { - ConsumerSet = concurrent.NewSet() -} - -// MessageHandler is a function for handling stream messages -type MessageHandler func(ctx context.Context, stream, id string, data []byte) error - -// StreamClient is the generic type inferface for a client of redis streams -type StreamClient interface { - // CreateStream if it does not already exist - CreateStream(ctx context.Context, stream, consumerGroup string) error - // AddMessges to the specified stream - AddMessages(ctx context.Context, stream string, message ...interface{}) error - // ReadAndHandle any messages for the specified consumer, including any retries - ReadAndHandle(ctx context.Context, stream, consumerGroup, consumerID string, handle MessageHandler, batchSize int64) - // UnacknowledgedCount returns the count of messages which are either unread or pending - UnacknowledgedCounts(ctx context.Context, stream, consumerGroup string) (lag int64, pending int64, err error) - // GetStreamLength returns the stream length - GetStreamLength(ctx context.Context, stream string) (int64, error) - // GetMessageRetryAfter returning true if a retry-after existing for message id - GetMessageRetryAfter(ctx context.Context, id string) (bool, error) - // SetMessageRetryAfter for message id, expiring after delay - SetMessageRetryAfter(ctx context.Context, id string, delay time.Duration) error - // IncrementSetScore increments the score of an element in a redis sorted set by 1 - IncrementSetScore(ctx context.Context, set, element string) error -} - -// RedisClient is an implementation of StreamClient using an actual redis connection -type RedisClient redis.Client - -func NewStreamClient(ctx context.Context, env, addr, user, pass string, useTLS bool) (*RedisClient, error) { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return nil, err - } - - var tlsConfig *tls.Config - if useTLS { - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: 0, - } - } - - // only if environment is local do we hardcode these values - if tlsConfig != nil && env == "local" { - certPool := x509.NewCertPool() - pem, err := ioutil.ReadFile(filepath.Join(rootdir.Path, "./libs/redisconsumer/tests/tls/ca.crt")) - if err != nil { - return nil, fmt.Errorf("failed to read test-mode ca.crt: %w", err) - } - certPool.AppendCertsFromPEM(pem) - tlsConfig.RootCAs = certPool - } - rc := redis.NewClient( - &redis.Options{ - Addr: addr, Password: pass, Username: user, - DialTimeout: 15 * time.Second, - WriteTimeout: 5 * time.Second, - MaxRetries: 5, - MinRetryBackoff: 5 * time.Millisecond, - MaxRetryBackoff: 500 * time.Millisecond, - TLSConfig: tlsConfig, - }, - ) - - _, err = rc.Ping(ctx).Result() - if err != nil { - return nil, fmt.Errorf("failed to setup redis client: %w", err) - } - logger.Info().Msg("ping success, redis client connected") - - return (*RedisClient)(rc), nil -} - -// CreateStream if it does not already exist -func (redisClient *RedisClient) CreateStream(ctx context.Context, stream, consumerGroup string) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return err - } - - err = redisClient.XGroupCreateMkStream(ctx, stream, consumerGroup, "0").Err() - if err != nil && !strings.Contains(err.Error(), "BUSYGROUP") { - logger.Error().Err(err).Msg("XGROUP CREATE MKSTREAM failed") - return err - } - return nil -} - -// AddMessage to the specified redis stream -func (redisClient *RedisClient) AddMessages(ctx context.Context, stream string, messages ...interface{}) error { - pipe := ((*redis.Client)(redisClient)).Pipeline() - - // to avoid errors enqueuing large message sets, enqueue them in chunks - for _, messages := range chunkMessages(100, messages) { - // loop again so that each message gets its own record - for _, message := range messages { - pipe.XAdd( - ctx, &redis.XAddArgs{ - Stream: stream, - Values: map[string]interface{}{ - "data": message}}, - ) - } - _, err := pipe.Exec(ctx) - if err != nil { - return err - } - } - return nil -} - -func chunkMessages(chunkSize int, messages []interface{}) [][]interface{} { - total := len(messages) - if total == 0 { - return nil - } - - result := make([][]interface{}, 0, 1) - for start, stop := 0, 0; start < total; start = stop { - stop += chunkSize - if stop > total { - stop = total - } - - result = append(result, messages[start:stop]) - } - return result -} - -// ReadAndHandle any messages for the specified consumer, including any retries -func (redisClient *RedisClient) ReadAndHandle(ctx context.Context, stream, consumerGroup, consumerID string, handle MessageHandler, batchSize int64) { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return - } - - readAndHandle := func(id string) int { - entries, err := redisClient.XReadGroup(ctx, &redis.XReadGroupArgs{ - Group: consumerGroup, - Consumer: consumerID, - Streams: []string{stream, id}, - Count: batchSize, - Block: 100 * time.Millisecond, - NoAck: false, - }).Result() - if err != nil && !strings.Contains(err.Error(), "redis: nil") { - logger.Error().Err(err).Msg("XREADGROUP failed") - } - - if len(entries) > 0 { - var wg sync.WaitGroup - for i := 0; i < len(entries[0].Messages); i++ { - messageID := entries[0].Messages[i].ID - values := entries[0].Messages[i].Values - data, exists := values["data"] - if !exists { - logger.Error().Msg("data did not exist in message") - } - sData, ok := data.(string) - if !ok { - logger.Error().Msg("data was not a string") - } - - tmp := logger.With().Str("messageID", messageID).Logger() - logger = &tmp - ctx = logger.WithContext(ctx) - - wg.Add(1) - go func() { - defer wg.Done() - err := handle(ctx, stream, messageID, []byte(sData)) - if err != nil { - if !strings.Contains(err.Error(), "retry-after") { - logger.Warn().Err(err).Msg("message handler returned an error") - } - } else { - redisClient.XAck(ctx, stream, consumerGroup, messageID) - } - }() - } - wg.Wait() - } - - return len(entries) - } - // first read and handle new messages - n := readAndHandle(">") - if n == 0 { - // then read and handle pending messages once there are no more new messages - readAndHandle("0") - } -} - -// UnacknowledgedCounts returns the count of messages which are either unread or pending -// NOTE: this is only accurate if no messages were deleted -func (redisClient *RedisClient) UnacknowledgedCounts(ctx context.Context, stream, consumerGroup string) (lag int64, pending int64, err error) { - err = redisClient.CreateStream(ctx, stream, consumerGroup) - if err != nil { - return -1, -1, err - } - - resp, err := redisClient.XInfoGroups(ctx, stream).Result() - if err != nil { - return -1, -1, err - } - for _, group := range resp { - if group.Name == consumerGroup { - return group.Lag, group.Pending, nil - } - } - return -1, -1, errors.New("unable to find specified group") -} - -// GetStreamLength returns the stream length -func (redisClient *RedisClient) GetStreamLength(ctx context.Context, stream string) (int64, error) { - return redisClient.XLen(ctx, stream).Result() -} - -// GetMessageRetryAfter returning true if a retry-after existing for message id -func (redisClient *RedisClient) GetMessageRetryAfter(ctx context.Context, id string) (bool, error) { - err := redisClient.Get(ctx, RetryAfterPrefix+id).Err() - if err == nil { - return true, nil - } - if err != redis.Nil { - return false, err - } - - return false, nil -} - -// SetMessageRetryAfter for message id, expiring after delay -func (redisClient *RedisClient) SetMessageRetryAfter(ctx context.Context, id string, delay time.Duration) error { - return redisClient.Set(ctx, RetryAfterPrefix+id, "", delay).Err() -} - -// IncrementSetScore increments the score of an element in a redis sorted set by 1 -func (redisClient *RedisClient) IncrementSetScore(ctx context.Context, set, element string) error { - return redisClient.ZIncrBy(ctx, set, 1, element).Err() -} - -// StartConsumer using a generic stream client -// NOTE: control will remain in this function until context cancellation -func StartConsumer(ctx context.Context, streamClient StreamClient, stream, consumerGroup, consumerID string, handle MessageHandler, batchSize int64) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return err - } - - consumerUID := stream + consumerGroup + consumerID - if !ConsumerSet.Add(consumerUID) { - // Another identical consumer is already running - return nil - } - defer ConsumerSet.Remove(consumerUID) - - ctx, logger = logging.UpdateContext(ctx, logger.With().Str("stream", stream).Str("consumerGroup", consumerGroup).Str("consumerID", consumerID).Logger()) - - logger.Info().Msg("consumer started") - - streamClient.CreateStream(ctx, stream, consumerGroup) - if err != nil { - return err - } - - ticker := time.NewTicker(100 * time.Millisecond) - for { - select { - case <-ticker.C: - streamClient.ReadAndHandle(ctx, stream, consumerGroup, consumerID, handle, batchSize) - case <-ctx.Done(): - logger.Info().Msg("shutting down consumer") - return nil - } - } -} diff --git a/libs/redisconsumer/consumer_test.go b/libs/redisconsumer/consumer_test.go deleted file mode 100644 index 9a95ffa56..000000000 --- a/libs/redisconsumer/consumer_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package redisconsumer - -import ( - "testing" - - must "github.com/stretchr/testify/require" -) - -func TestChunkMessagesEmpty(t *testing.T) { - var testMessages []interface{} - - testChunks := chunkMessages(500, testMessages) - must.Equal(t, 0, len(testChunks)) -} - -func TestChunkMessagesSingleChunk(t *testing.T) { - var testMessages []interface{} - - for i := 0; i < 500; i++ { - testMessages = append(testMessages, i) - } - testChunks := chunkMessages(500, testMessages) - must.Equal(t, 1, len(testChunks)) - must.Equal(t, 500, len(testChunks[0])) - - // test that we have everything we expect in the right order and chunks - for i, chunk := range testChunks { - if i == 0 { - for ii := 0; ii < 500; ii++ { - must.Equal(t, chunk[ii], testMessages[ii]) - } - } - } -} - -func TestChunkMessagesMultipleChunks(t *testing.T) { - var testMessages []interface{} - for i := 0; i < 1000; i++ { - testMessages = append(testMessages, i) - } - testChunks := chunkMessages(500, testMessages) - must.Equal(t, 2, len(testChunks)) - must.Equal(t, 500, len(testChunks[0])) - must.Equal(t, 500, len(testChunks[1])) - - for i, chunk := range testChunks { - if i == 0 { - for ii := 0; ii < 500; ii++ { - must.Equal(t, chunk[ii], testMessages[ii]) - } - } - if i == 1 { - for ii := 0; ii < 500; ii++ { - must.Equal(t, chunk[ii], testMessages[ii + 500]) - } - } - } -} - -func TestChunkMessagesOverflow(t *testing.T) { - var testMessages []interface{} - - for i := 0; i < 1100; i++ { - testMessages = append(testMessages, i) - } - testChunks := chunkMessages(500, testMessages) - must.Equal(t, 3, len(testChunks)) - must.Equal(t, 500, len(testChunks[0])) - must.Equal(t, 500, len(testChunks[1])) - must.Equal(t, 100, len(testChunks[2])) - - for i, chunk := range testChunks { - if i == 0 { - for ii := 0; ii < 500; ii++ { - must.Equal(t, chunk[ii], testMessages[ii]) - } - } - if i == 1 { - for ii := 0; ii < 500; ii++ { - must.Equal(t, chunk[ii], testMessages[ii + 500]) - } - } - if i == 2 { - for ii := 0; ii < 100; ii++ { - must.Equal(t, chunk[ii], testMessages[ii + 1000]) - } - } - } -} diff --git a/libs/redisconsumer/gen-test-certs.sh b/libs/redisconsumer/gen-test-certs.sh deleted file mode 100755 index 1d76160a2..000000000 --- a/libs/redisconsumer/gen-test-certs.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -# Generate some test certificates which are used by the regression test suite: -# -# tests/tls/ca.{crt,key} Self signed CA certificate. -# tests/tls/redis.{crt,key} A certificate with no key usage/policy restrictions. -# tests/tls/client.{crt,key} A certificate restricted for SSL client usage. -# tests/tls/server.{crt,key} A certificate restricted for SSL server usage. -# tests/tls/redis.dh DH Params file. - -generate_cert() { - local name=$1 - local cn="$2" - local opts="$3" - - local keyfile=tests/tls/${name}.key - local certfile=tests/tls/${name}.crt - - cat > tests/tls/${name}.cnf <<_END_ -[req] -distinguished_name = req_distinguished_name -req_extensions = v3_req -prompt = no - -[req_distinguished_name] -O = Redis Test -CN = ${cn} - -[v3_req] -keyUsage = keyEncipherment, dataEncipherment -extendedKeyUsage = serverAuth -subjectAltName = @alt_names - -[alt_names] -IP.1 = 127.0.0.1 -DNS.2 = redis -_END_ - - [ -f $keyfile ] || openssl genrsa -out $keyfile 2048 - openssl req \ - -new -sha256 \ - -key $keyfile \ - -config tests/tls/${name}.cnf | \ - openssl x509 \ - -req -sha256 \ - -CA tests/tls/ca.crt \ - -CAkey tests/tls/ca.key \ - -CAserial tests/tls/ca.txt \ - -CAcreateserial \ - -days 3650 \ - $opts \ - -out $certfile -} - -mkdir -p tests/tls -[ -f tests/tls/ca.key ] || openssl genrsa -out tests/tls/ca.key 4096 -openssl req \ - -x509 -new -nodes -sha256 \ - -key tests/tls/ca.key \ - -days 3650 \ - -subj '/O=Redis Test/CN=Certificate Authority' \ - -out tests/tls/ca.crt - -cat > tests/tls/openssl.cnf <<_END_ -[ server_cert ] -keyUsage = digitalSignature, keyEncipherment -nsCertType = server -subjectAltName = DNS:redis,IP:127.0.0.1 - -[ client_cert ] -keyUsage = digitalSignature, keyEncipherment -nsCertType = client -_END_ - -generate_cert server "Server-only" "-extfile tests/tls/openssl.cnf -extensions server_cert" diff --git a/libs/redisconsumer/privkey.pem b/libs/redisconsumer/privkey.pem deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/redisconsumer/tests/README.md b/libs/redisconsumer/tests/README.md deleted file mode 100644 index 40b668dfd..000000000 --- a/libs/redisconsumer/tests/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# bring up redis -``` -docker-compose up -``` - -# connect via redis-cli -``` -redis-cli -p 6380 --tls --cacert tls/ca.crt -``` diff --git a/libs/redisconsumer/tests/docker-compose.yml b/libs/redisconsumer/tests/docker-compose.yml deleted file mode 100644 index 90a81f2c3..000000000 --- a/libs/redisconsumer/tests/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: "3.4" - -services: - redis-master: - container_name: redis-master - image: bitnami/redis:7.2 - environment: - - REDIS_ACLFILE=/etc/redis/users.acl - - ALLOW_EMPTY_PASSWORD=yes - - REDIS_PORT=0 - - REDIS_TLS_ENABLED=yes - - REDIS_TLS_PORT=6379 - - REDIS_TLS_AUTH_CLIENTS=no - - REDIS_TLS_CERT_FILE=/etc/redis/server.crt - - REDIS_TLS_KEY_FILE=/etc/redis/server.key - - REDIS_TLS_CA_FILE=/etc/redis/ca.crt - ports: - - "6380:6379" - volumes: - - ./tls:/etc/redis diff --git a/libs/redisconsumer/tests/tls/ca.crt b/libs/redisconsumer/tests/tls/ca.crt deleted file mode 100644 index 6fccd2c6b..000000000 --- a/libs/redisconsumer/tests/tls/ca.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFSzCCAzOgAwIBAgIUGwPYcmyAn41eoaONCuRm399069EwDQYJKoZIhvcNAQEL -BQAwNTETMBEGA1UECgwKUmVkaXMgVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUg -QXV0aG9yaXR5MB4XDTIzMTAwMjIwMDExNFoXDTMzMDkyOTIwMDExNFowNTETMBEG -A1UECgwKUmVkaXMgVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5 -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw4oOKvka9i/JLbMg5Maw -ritQL9Rb5+ppXn/oZjh9Rba7VGuDgxahaX9RAU4lonT1x1jygJMpL9nQ2Bsa0trY -q4SUBs1KEWcC4HQGsrJG9yaxuoy43BlEQccF2v6YHt4aS6/YzGyFEp4kEEJhXtWE -CSBH5FVczPRcOVPMl8YMLMMKxMF9ZS0Y/PUkr98O2hujiKg8eK2CMqfHoDgyKA+1 -qFvVKBN4PhspnnpPXIgnmyECqvG0CumDUUd6WoO/nHht4lkgHu5g8zBtfDkVg8hW -UDi1KY4THnTDPDE6tXN/eYzf3s7fJkInhRK0k8vzX3QbR9q2hfG9SWmYGu2h0Lp3 -3VtGOiBgXYFWqYptN+r8k8t+xRwFVet8itfhkgvhk/tFxO4awqx+eZiH3mlVzVVP -S8JD8rdtEsK4hQQRTG1XC5zhNCrGrjGsJdfHl0R57M/Rd9o2Qp7V4hQC3h07o8ts -5T1wBeq4WxpYh1OIADQHATl38O/05vipOD9ZaZ5IFBfXr2T2ofafejMduK/29Kal -k+YmPLvPI0j1BXZq3sBhaLO64Qc0sk8742qVVnO9hLXcN7mtqqNFupALscTkY4he -dX4wh4vL085Y+D4Ew+h0quLZAq3k47GNA3VQWXy346GCVZAvOxJB1gPvSpVAF1cu -iCN2ox4xdi5nRLzqrpF1/PsCAwEAAaNTMFEwHQYDVR0OBBYEFHJ/GeNqfwAAfwzx -jdGEPisKR3J4MB8GA1UdIwQYMBaAFHJ/GeNqfwAAfwzxjdGEPisKR3J4MA8GA1Ud -EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAFdCrbPVGgn8sjysGtsvwvY0 -f4C6MZZiFWvqAJN8pLqvXsfwScEtHd/d+f6RUT8wwnXw79QtGbKJTffHy8aj68rk -hkSiREYnjydS3gjQ8k6fhCL41n8QoUtK0w3VVYVVIUI6eJhs3yD+JaetBtKtlGIS -XqhV1DUuAkYdGaBUCgUEDOMPDnoIA1BsS1liiDWnMIyMOZNaJHRKh/5gVoVUu1Wh -stYJ7Oo1IxbBiDf3VcClg+eqTw8CoSZ2LE/tOWhvkr6Jd4kD4J9gsxcE0xb14p3Z -xdFNsQJNzIjFED7LzmtTnA0uVnttI4Wht/R/qoCghdO8LeKlLsI6JGpeHcEiw9zP -njW2wORXSNlbBT1FHXwrhhlQaP/wMWn5pgMny0kVins6vvEcX+QjwVS5DALmbu8p -Ij7wK2RZ8wOOhZH35eU3WbzUdXijpWFoe22QOa3WBFDyNg+eQx7jPbYjCusnKZs9 -Bz4uiMX36csu10dGXMaf/4zzs2uRPgTKppyctNY+tVm8cqn6nhg7IkcOEdo27E0b -r0SmG5R0Mwd93W5A5PLekUD0emswrTLcubvVOD5l+d+KT47YUZVXuF73PV4GVcoe -alxLbRoWhFEfLANUKoK6Uw0Zj/lnJ1CCq2Q7aYnsQJAgOiAZ6lZhMqLTfiZth0m+ -29itEj3SAVNttZpA69ft ------END CERTIFICATE----- diff --git a/libs/redisconsumer/tests/tls/ca.key b/libs/redisconsumer/tests/tls/ca.key deleted file mode 100644 index 65438d98d..000000000 --- a/libs/redisconsumer/tests/tls/ca.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDDig4q+Rr2L8kt -syDkxrCuK1Av1Fvn6mlef+hmOH1FtrtUa4ODFqFpf1EBTiWidPXHWPKAkykv2dDY -GxrS2tirhJQGzUoRZwLgdAayskb3JrG6jLjcGURBxwXa/pge3hpLr9jMbIUSniQQ -QmFe1YQJIEfkVVzM9Fw5U8yXxgwswwrEwX1lLRj89SSv3w7aG6OIqDx4rYIyp8eg -ODIoD7WoW9UoE3g+Gymeek9ciCebIQKq8bQK6YNRR3pag7+ceG3iWSAe7mDzMG18 -ORWDyFZQOLUpjhMedMM8MTq1c395jN/ezt8mQieFErSTy/NfdBtH2raF8b1JaZga -7aHQunfdW0Y6IGBdgVapim036vyTy37FHAVV63yK1+GSC+GT+0XE7hrCrH55mIfe -aVXNVU9LwkPyt20SwriFBBFMbVcLnOE0KsauMawl18eXRHnsz9F32jZCntXiFALe -HTujy2zlPXAF6rhbGliHU4gANAcBOXfw7/Tm+Kk4P1lpnkgUF9evZPah9p96Mx24 -r/b0pqWT5iY8u88jSPUFdmrewGFos7rhBzSyTzvjapVWc72Etdw3ua2qo0W6kAux -xORjiF51fjCHi8vTzlj4PgTD6HSq4tkCreTjsY0DdVBZfLfjoYJVkC87EkHWA+9K -lUAXVy6II3ajHjF2LmdEvOqukXX8+wIDAQABAoICAD702ZfraUtfWlIM7OfLswH4 -rxIfONIEDpXe78fNo0PJUCofjZGsvR523/FgJEzYhHOBB17vQqEF67BQlcPSs1UR -1AkWmqTcujfjE28AaEnV2v45paEzv17AQfne48J4dxXsUYyL2hKC+RCFFs2OSDsZ -+huhgkX43SKWAPbSzIOy7mon9AQZz4RON1gqpKEDqhTrOcEO+C7wBx1mSnvMhwvk -OU50ak/wtecw8RFVQw82wyGfvXyP73hBX7r51Tz/Jz3rvSRIUs2px25qAKvT2jps -76Mn+67gICXmocTfIWlUupWFnKI4RXoaaip1zW9rtgbd+8tHaE7VISR2YxWNm96H -8IwYuS2JPq1XKkEeHyNiMV+AqLy+mewTUC6YI7FLewoEBRpguIdOoLXxQijSp5+m -GjY1CgG0hhqOo/zzxUNQRPkLlXrk7GuTFgEQXC4/ciVjWeOIuhDez6aOkM8dB44N -V9TJTTP5PqSTWcbLA43SDFka/zNnoIqHjDpQFFxsbVe48RepjSc9E3aOFSAGCglf -87sW0+SRJVYuslz8t7FDEwhZXNs8yh6h2uZc5G0q3G0g6BZRX0F30Reyyadwh4/N -xTn+LBYpzoOCnpECcEwxFYkPlH1mLxKyB/W+kvRkKjqnw5NU48Y3qCaiusP00t8v -ZZDV+WQwx03HcL5VQWzZAoIBAQDLI3u6ZJkFBXyKn9qZPBjNXKiDe9OBWylXDM52 -xOiFZ6P2Wq9IkUTsQS03pgPR2iKUAXbiinORjCwGCJiRSVZ+a3pX0D52gGWZVXr5 -rE2CI8/v8CL9pJwhotZcRAJIU2GjhBgC0Tjdbp07gJYXDox7foWaAuLnZn7iMlfi -XYpg/sFkoS5oNQARTftP9ozLD7fM1bKVB7g+iZSvlisrFWa2d1CNuFW2A01zr+Nr -Z562ZOy48jD/cQmBjcvn06a5XVVu3onhjZEe3mW03Ic+Ni2pfGCjz1tAmWfog9gy -Egp8mHNa1kwkURrk1DnH9wFzGdRABeNvOsxFNOshLavAZbpzAoIBAQD2bFOOmwOy -SRnCVpdAzL8KFi1Wk4zYJrLkBDuyDnONWhwmhn8dveB6aWVlr1ly2ufZ2aGzKqrO -+c2R2UUQMKK87IXMsilbtZGypighB0GJWlg6vq9LUvhGzFssczKG2La/Jr1YIkai -xR52qFMxBvsELrosjUs1OlOYmrkjsdllfYEK7wc+ELAImpw9pwyW9Skxx0E8JoNl -Y8NG1VYSVmMmBOna9g+Mw0W6HlqR8Bg4/Z38CmAmdzgpi9y0e/eU9PPPFCq+hYWw -Ib24MAMlKn5MmeG3B/Nz/CsixyITdIADEA3r61JlwLKJ5Zal7E6DB5sLR70GpdXo -NTyiGG52JWlZAoIBAQC1Vs+07OjM2sD63wi9kjBx4y1rcra02zRkSUJmNBF+Ra7Q -1NN07HmijexXqRPv2dLUXRpL0VypqYADQtkvmVBlOnzUt0mLb8NCxlQpXe8zJdUS -VERoOPJH9J13cB65gfW/isQEf9FLmtsxqvBRVOODusZ5XepAt/a85kQeB3Loc4Mg -2eIblHSi/gJCt82n2Dgz2vC4qVOIQDlDJcMTit9OM+TtHoklGn2hsHpL/wxntmC8 -GTg0UCw0Quir4v0KrI7/khl0CHRsIgyZD+SLXMIWcrmyggDsAgYIQupUiFZkM5OG -m5cNNHnVnL6YC6B3p68aGCeXu91uSnc7/xE7h5SRAoIBAQDp1pXb50G0tyWYgu74 -8NIAUlcnptCqdN6g/VtgddI1OmMoM80AfiQ8RTORMLpjAVOuKJUHHeAElIv8cP7W -hDCJsrCtNAuHGV9u9blyzh3kpzS+WiCiC9PJ6zZBkfdKeu+/qJVYwv3gi8mCs+xt -w8ADkgvdJx9NHVIiqAAz3Rr88gG4TGbFALTrFTxaTODnhzFWKUzANZPObMLrvpn0 -wMFJXLRFseXDn7HVEeTEQ9/YhOKcP4RKPCuxUSVYp6KjWWbbBLojHTVllD8iP31c -kVxUeFn2g7cEQzDzyZ+OcqjfPybWzvHb2OYTcShpvzopoNgB9ktruYao2Yv2DdUU -jB/RAoIBAQCi5nsecFGAHGQm+9Ox+9ddatjjeRROnGBUcw0Z2q1S0ZyZhHwQ25NW -HanRBgGYan0Z+t9hhzoCTlsGcs+KUlIAu6+UB+30OtviEyDMKDkSx3e/k7wkAuV6 -W60Yx29pU3s664FiuZLiJ3tRRr0RtEHQGkiqlARf61mzxJgZclBTRuRAXp5LNZ4a -mKhx9BGwh96NTMpRGhFiHm8YI0wB91crw3iUpuQo6agpjRHe55AlF1Xcciu3P70G -oW1i+TOj208PDeBINyAi/H/wG+akZQAAl18aSb8ycgiE/8xjJoSZQfVHKefzlxe5 -bTXBoS8ewel9uHY0MYbm7lSt4lP+mQVf ------END PRIVATE KEY----- diff --git a/libs/redisconsumer/tests/tls/ca.txt b/libs/redisconsumer/tests/tls/ca.txt deleted file mode 100644 index cde6aee4a..000000000 --- a/libs/redisconsumer/tests/tls/ca.txt +++ /dev/null @@ -1 +0,0 @@ -2D83F050647731E79AE445D0F7FD6C96A49438ED diff --git a/libs/redisconsumer/tests/tls/openssl.cnf b/libs/redisconsumer/tests/tls/openssl.cnf deleted file mode 100644 index 13c0e2fdd..000000000 --- a/libs/redisconsumer/tests/tls/openssl.cnf +++ /dev/null @@ -1,8 +0,0 @@ -[ server_cert ] -keyUsage = digitalSignature, keyEncipherment -nsCertType = server -subjectAltName = DNS:redis,IP:127.0.0.1 - -[ client_cert ] -keyUsage = digitalSignature, keyEncipherment -nsCertType = client diff --git a/libs/redisconsumer/tests/tls/server.cnf b/libs/redisconsumer/tests/tls/server.cnf deleted file mode 100644 index d2b7670ea..000000000 --- a/libs/redisconsumer/tests/tls/server.cnf +++ /dev/null @@ -1,17 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -req_extensions = v3_req -prompt = no - -[req_distinguished_name] -O = Redis Test -CN = Server-only - -[v3_req] -keyUsage = keyEncipherment, dataEncipherment -extendedKeyUsage = serverAuth -subjectAltName = @alt_names - -[alt_names] -IP.1 = 127.0.0.1 -DNS.2 = redis diff --git a/libs/redisconsumer/tests/tls/server.crt b/libs/redisconsumer/tests/tls/server.crt deleted file mode 100644 index dffc0026a..000000000 --- a/libs/redisconsumer/tests/tls/server.crt +++ /dev/null @@ -1,26 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEaDCCAlCgAwIBAgIULYPwUGR3Meea5EXQ9/1slqSUOO0wDQYJKoZIhvcNAQEL -BQAwNTETMBEGA1UECgwKUmVkaXMgVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUg -QXV0aG9yaXR5MB4XDTIzMTAwMjIwMDExNFoXDTMzMDkyOTIwMDExNFowKzETMBEG -A1UECgwKUmVkaXMgVGVzdDEUMBIGA1UEAwwLU2VydmVyLW9ubHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCLPkVjia2ZH1P+GQYXDNtt73eMRG6Y7plW -jY8cMxVWTLgLZl95uCSP3N2gG4U1i6gOl1fQhm5gbsQzuzBWwxVujkVlyq8Rplfj -vR7qVjNrMttsHSCSvc8QSlYCIsbP+Rct/Ry8378hbd8jaMT3XXU55HhmU1eabOfh -vdN2zFotSgZFVW/jYjuTLEdNiFDxlifCFVWH1xwW6F9r2zADP9E79WtHDlqmI5Ga -HbdWyc7IKnOjsSudiEN5oeKxtFbOQ0rzB/wHuajiMWxpMf3DIbN8oTCFZwu8ynng -rK/vUebAv+K/BunThdYvpQL9IhJhfn8dHFz/CWpyR5XoFlO4tBJHAgMBAAGjejB4 -MAsGA1UdDwQEAwIFoDARBglghkgBhvhCAQEEBAMCBkAwFgYDVR0RBA8wDYIFcmVk -aXOHBH8AAAEwHQYDVR0OBBYEFG+tEuZLkvIWzlfHEqGoJ/dYOBxIMB8GA1UdIwQY -MBaAFHJ/GeNqfwAAfwzxjdGEPisKR3J4MA0GCSqGSIb3DQEBCwUAA4ICAQBreM6a -QW6pAXxqc+DPnmNs30OrmhGQhumvNXA5AvqwGiiCMFlavdRTMnE2JGeL0TAdKhlY -BIrptbi5N0Qxg1xPHkwmZNADLix6zkr664rHieMYSJDNt6tNwnexHRtVftxH4GZz -dqNYRjQYPq9c7emnLOBa5XGfBjh95T+ptHSoxsSVH8LGftWJ+pwmx6TewI6LPUdn -kqXov8Lo5WQH3Zsa9caiFEQUVU/shfkBxR3J34hCTu1dHnuhA/nG+Ds/8pDZg8hN -iQXxiB35jDrtgSiY6zezmJDS1x5F0CLGFj2mIZVg2/Tk4GdPnpyBjZW0MXW32ium -DWgICMEbMFmBYeBlVQKxy8cbJszzVs/FJU3LvOG0xYyk7HAkpKbILvU2cald9KYh -w27a4cUqiLUv4+7A3LjP/lleDG1EkCOHZdDPt8vMsPSxFpVKR2203IheHOPSBqsT -FChiiedY7cgeLbCMaOygBACKTGYYFJmExsxufT5YgVAAxrcT1dibFg6/ujXTzQ/7 -oUZOqGMzdckqv+6CmObc8M/P8xrWKhpEg3qUu+o4RzTP9Efjaun8cxIGkOHPM7ph -rlabH6cRUdaReU4T34EKPWzycDWcuBXlqALzZNYpooAqdt1nZ0ugHcJYIJFQ709/ -cchvoPo1Y+1zPl6jEXdCLk3ZhBjusuhWRqepRQ== ------END CERTIFICATE----- diff --git a/libs/redisconsumer/tests/tls/server.key b/libs/redisconsumer/tests/tls/server.key deleted file mode 100644 index 5669c950d..000000000 --- a/libs/redisconsumer/tests/tls/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCLPkVjia2ZH1P+ -GQYXDNtt73eMRG6Y7plWjY8cMxVWTLgLZl95uCSP3N2gG4U1i6gOl1fQhm5gbsQz -uzBWwxVujkVlyq8RplfjvR7qVjNrMttsHSCSvc8QSlYCIsbP+Rct/Ry8378hbd8j -aMT3XXU55HhmU1eabOfhvdN2zFotSgZFVW/jYjuTLEdNiFDxlifCFVWH1xwW6F9r -2zADP9E79WtHDlqmI5GaHbdWyc7IKnOjsSudiEN5oeKxtFbOQ0rzB/wHuajiMWxp -Mf3DIbN8oTCFZwu8ynngrK/vUebAv+K/BunThdYvpQL9IhJhfn8dHFz/CWpyR5Xo -FlO4tBJHAgMBAAECgf9YBdrXkbFW8ExTKn6EaRxN4ukv7Wf88C2ptCiZKwcnXFhR -hzspNxSQh+Q9J9M4hjs2/iAFMkuUVRwIQnfMYTFV3O+slEnCXpAw54fORlqQPxYh -bPL/0Kg/zr468METDTlvWRKm/h//10pimANBB1OTxVpXVVg2caIkklubkz4ZDO5f -TvrkDYZCcj57YceeDjZi9qd+EuBAABwZQ4KbtKFtPxDzCpkR6uMpJ7LWlEbQDvU2 -/v894E87hPLfVTIdHq3fjOQN1iIyAsW+0FjH52NjDL4DMeGdUABZhBLZnT+8z7N5 -rS4NTz+Oye5pAio58nieyzAPtz9ReOcMS7e+Y5kCgYEAxAOs7hAioHadUJXqQeQw -SO7osONU3YXB2ua6NoxqJrtTdB37p9V9sEFT7/lxnDBCkip6018IkHhNiLLSY8yW -VZdndPsQXjwa996uIgVp5HEWy2FNPVvFLFMWQlGSPbrSxe9VQfePG/oEnT0grlZo -Jpu8gLZ47lrDliGWkanqhmkCgYEAtdr8DYF3XoGUwhoFhW4nINPzXg4iOMgvLRi0 -+Jy40IWSr2cfitQNCIGWECQ4pGVIeW2vSV4Su4iWPW31nWf/ix6gHBuX25qFw8G+ -gWylWYbC3+bFWAo9AzZLJZbcPbCsWlCL+PbfULKJNsUcKe72qM+FmhLu6Kj23PEo -3J+fnS8CgYEAhNKcxwCmBif6N9YuVBIFahaCFQMmyalOkD/SpQ4HDFHZnhCHK9Zu -AhZyg2V7R8QoCBXC/yIhkowCtgO+ryO+JyeeUlWiZMjD2XzKcIFMnSDIum4nwdFC -zeNLbQsLVssW8ktYcYgIMIP9xnLah2hD0Q2s8Wc0kQduC3idJ/2EzykCgYBXzUUI -2Etj67iBBsNHqullZjAIZ1aKh8yPP3CQ4EozE2rBGU8XXk50nuxvGq3mYaheSGDr -UWpF9SpkumRt/TpaVw0CxSfKtY6D91Udc/FZikEojWkWmsredCZQHch0WIq+iEks -iTVE5w5szTpN3LxmwNtGioGi/4FBJ4aNDtynrQKBgCa7xBM3fSuNX/1rc8OPTRVN -koks76yNe8A0oJat5DlUBqBKzfGVif1ah+68MjI0e70bhEzbAQQtyqNkZtRPsWfR -laA282kffjXVsfaIasDSkwIMaTyGN/jMT93XS2KJ+sw442vJZh70DZfoW1HK6U97 -rPj+1Npc10OfDHC/dPyW ------END PRIVATE KEY----- diff --git a/libs/redisconsumer/tests/tls/users.acl b/libs/redisconsumer/tests/tls/users.acl deleted file mode 100644 index fd7759dbf..000000000 --- a/libs/redisconsumer/tests/tls/users.acl +++ /dev/null @@ -1 +0,0 @@ -user redis on #34fb46c847bb9df96e5205a39d382f648a6e8dce1e014cd85b4ca6a88d88ed03 ~& +@all ~* diff --git a/libs/requestutils/requestutils.go b/libs/requestutils/requestutils.go deleted file mode 100644 index 41c14dc8e..000000000 --- a/libs/requestutils/requestutils.go +++ /dev/null @@ -1,77 +0,0 @@ -package requestutils - -import ( - "context" - "encoding/json" - "errors" - "io" - "io/ioutil" - "net/http" - - "github.com/brave-intl/bat-go/libs/closers" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/logging" -) - -type requestID string - -var ( - payloadLimit10MB = int64(1024 * 1024 * 10) - // RequestIDHeaderKey is the request header key - RequestIDHeaderKey = "x-request-id" - // RequestID holds the type for request ids - RequestID = requestID(RequestIDHeaderKey) - // HostHeaderKey is the request header key - HostHeaderKey = "host" - // XForwardedHostHeaderKey is the request header key - XForwardedHostHeaderKey = "x-forwarded-host" -) - -// ReadWithLimit reads an io reader with a limit and closes -func ReadWithLimit(ctx context.Context, body io.Reader, limit int64) ([]byte, error) { - defer closers.Panic(ctx, body.(io.Closer)) - return ioutil.ReadAll(io.LimitReader(body, limit)) -} - -// Read an io reader -func Read(ctx context.Context, body io.Reader) ([]byte, error) { - jsonString, err := ReadWithLimit(ctx, body, payloadLimit10MB) - if err != nil { - return nil, errorutils.Wrap(err, "error reading body") - } - return jsonString, nil -} - -// ReadJSON reads a request body according to an interface and limits the size to 10MB -func ReadJSON(ctx context.Context, body io.Reader, intr interface{}) error { - logger := logging.Logger(ctx, "requestutils.ReadJSON") - if body == nil { - return errorutils.New(errors.New("body is nil"), "Error in request body", nil) - } - jsonString, err := Read(ctx, body) - if err != nil { - return err - } - logger.Debug().Str("json", string(jsonString)).Msg("read payload") - err = json.Unmarshal(jsonString, &intr) - if err != nil { - return errorutils.Wrap(err, "error unmarshalling body") - } - return nil -} - -// SetRequestID transfers a request id from a context to a request header -func SetRequestID(ctx context.Context, r *http.Request) { - id := GetRequestID(ctx) - if id != "" { - r.Header.Set(RequestIDHeaderKey, id) - } -} - -// GetRequestID gets the request id -func GetRequestID(ctx context.Context) string { - if reqID, ok := ctx.Value(RequestID).(string); ok { - return reqID - } - return "" -} diff --git a/libs/responses/meta.go b/libs/responses/meta.go deleted file mode 100644 index f88a7a3c0..000000000 --- a/libs/responses/meta.go +++ /dev/null @@ -1,8 +0,0 @@ -package responses - -// Meta - generic api output metadata -type Meta struct { - Status string `json:"status,omitempty"` - Message string `json:"message,omitempty"` - Context map[string]interface{} `json:"context,omitempty"` -} diff --git a/libs/responses/pagination.go b/libs/responses/pagination.go deleted file mode 100644 index d248df001..000000000 --- a/libs/responses/pagination.go +++ /dev/null @@ -1,35 +0,0 @@ -package responses - -import ( - "context" - "encoding/json" - "fmt" - "net/http" -) - -// PaginationResponse - a response structure wrapper for pagination -type PaginationResponse struct { - Page int `json:"page,omitempty"` - Items int `json:"items,omitempty"` - MaxPage int `json:"max_page,omitempty"` - Ordered []string `json:"order,omitempty"` - Data interface{} `json:"data,omitempty"` -} - -// Render - render response -// response structure -// { page: 1, items: 50, max_page: 10, ordered: ["id", "..."], transactions: [...] } -func (pr *PaginationResponse) Render(ctx context.Context, w http.ResponseWriter, status int) error { - // marshal response - b, err := json.Marshal(pr) - if err != nil { - return fmt.Errorf("error encoding json response: %w", err) - } - - // write response - w.WriteHeader(status) - if _, err := w.Write(b); err != nil { - return fmt.Errorf("error writing response: %w", err) - } - return nil -} diff --git a/libs/rootdir/main.go b/libs/rootdir/main.go deleted file mode 100644 index b84b27096..000000000 --- a/libs/rootdir/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package rootdir - -import ( - "path/filepath" - "runtime" -) - -var ( - _, b, _, _ = runtime.Caller(0) - Path = filepath.Join(filepath.Dir(b), "../..") -) diff --git a/libs/service/secrets.go b/libs/service/secrets.go deleted file mode 100644 index c487d21b4..000000000 --- a/libs/service/secrets.go +++ /dev/null @@ -1,10 +0,0 @@ -package service - -import ( - "context" -) - -// SecretManager - interface which allows for secret discovery, management -type SecretManager interface { - RetrieveSecrets(context.Context, string) ([]byte, error) -} diff --git a/libs/service/service.go b/libs/service/service.go deleted file mode 100644 index d5978e53f..000000000 --- a/libs/service/service.go +++ /dev/null @@ -1,50 +0,0 @@ -package service - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/logging" - sentry "github.com/getsentry/sentry-go" -) - -// JobFunc - type that defines what a Job Function should look like -type JobFunc func(context.Context) (bool, error) - -// Job - Structure defining what a common job meta-information -type Job struct { - Func JobFunc - Workers int - Cadence time.Duration -} - -// JobService - interface defining what can have jobs -type JobService interface { - Jobs() []Job -} - -// JobWorker - a job worker -func JobWorker(ctx context.Context, job func(context.Context) (bool, error), duration time.Duration) { - logger := logging.Logger(ctx, "service.JobWorker") - for { - _, err := job(ctx) - if err != nil { - log := logger.Error().Err(err) - httpError, ok := err.(*errorutils.ErrorBundle) - if ok { - state, ok := httpError.Data().(clients.HTTPState) - if ok { - log = log.Int("status", state.Status). - Str("path", state.Path). - Interface("data", state.Body) - } - } - log.Msg("error encountered in job run") - sentry.CaptureException(err) - } - // regardless if attempted or not, wait for the duration until retrying - <-time.After(duration) - } -} diff --git a/libs/set/set.go b/libs/set/set.go deleted file mode 100644 index 7d8bf634a..000000000 --- a/libs/set/set.go +++ /dev/null @@ -1,93 +0,0 @@ -package set - -// Package set includes two set implementations, both backed by slices, one of -// which is safe. Intended for testing purposes - -import ( - "sync" -) - -// UnsafeSliceSet implements an unsafe slice backed set -type UnsafeSliceSet struct { - slice []string -} - -// NewUnsafeSliceSet creates a new UnsafeSliceSet -func NewUnsafeSliceSet() UnsafeSliceSet { - return UnsafeSliceSet{} -} - -// Cardinality returns the number of elements in the set -func (set *UnsafeSliceSet) Cardinality() (int, error) { - return len(set.slice), nil -} - -// Contains returns true if the given item is in the set -func (set *UnsafeSliceSet) Contains(e string) (bool, error) { - for _, a := range set.slice { - if a == e { - return true, nil - } - } - return false, nil -} - -// Add a single element to the set, return true if newly added -func (set *UnsafeSliceSet) Add(e string) (bool, error) { - r, err := set.Contains(e) - if err != nil { - panic(err) - } - if r { - return false, err - } - set.slice = append(set.slice, e) - return true, nil -} - -// Close the underlying connection to the datastore -func (set *UnsafeSliceSet) Close() error { - return nil -} - -// SliceSet implements an safe slice backed set -type SliceSet struct { - u *UnsafeSliceSet - sync.RWMutex -} - -// NewSliceSet creates a new SliceSet -func NewSliceSet() SliceSet { - tmp := NewUnsafeSliceSet() - return SliceSet{u: &tmp} -} - -// Cardinality returns the number of elements in the set -func (set *SliceSet) Cardinality() (int, error) { - set.RLock() - defer set.RUnlock() - return set.u.Cardinality() -} - -// Contains returns true if the given item is in the set -func (set *SliceSet) Contains(e string) (bool, error) { - set.RLock() - defer set.RUnlock() - return set.u.Contains(e) -} - -// Add a single element to the set, return true if newly added -func (set *SliceSet) Add(e string) (bool, error) { - set.Lock() - ret, err := set.u.Add(e) - if err != nil { - panic(err) - } - set.Unlock() - return ret, nil -} - -// Close the underlying connection to the datastore -func (set *SliceSet) Close() error { - return nil -} diff --git a/libs/set/set_test.go b/libs/set/set_test.go deleted file mode 100644 index df8f2c7eb..000000000 --- a/libs/set/set_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package set - -import ( - "testing" -) - -func TestUnsafeSliceSet(t *testing.T) { - set := NewUnsafeSliceSet() - if r, err := set.Add("FOO"); err != nil || !r { - t.Error("Add to empty set should always succeed") - } - if r, err := set.Contains("FOO"); err != nil || !r { - t.Error("Set should contain last added element") - } - - if r, err := set.Add("FOO"); err != nil || r { - t.Error("Re-add of same element should fail") - } - - if r, err := set.Add("BAR"); err != nil || !r { - t.Error("Add to empty set should always succeed") - } - if r, err := set.Contains("BAR"); err != nil || !r { - t.Error("Set should contain last added element") - } - if r, err := set.Contains("FOO"); err != nil || !r { - t.Error("Set should contain all added elements") - } - - if r, err := set.Cardinality(); err != nil || r != 2 { - t.Error("Set should contain contain 2 elements") - } -} - -func TestSliceSet(t *testing.T) { - set := NewSliceSet() - if r, err := set.Add("FOO"); err != nil || !r { - t.Error("Add to empty set should always succeed") - } - if r, err := set.Contains("FOO"); err != nil || !r { - t.Error("Set should contain last added element") - } - - if r, err := set.Add("FOO"); err != nil || r { - t.Error("Re-add of same element should fail") - } - - if r, err := set.Add("BAR"); err != nil || !r { - t.Error("Add to empty set should always succeed") - } - if r, err := set.Contains("BAR"); err != nil || !r { - t.Error("Set should contain last added element") - } - if r, err := set.Contains("FOO"); err != nil || !r { - t.Error("Set should contain all added elements") - } - - if r, err := set.Cardinality(); err != nil || r != 2 { - t.Error("Set should contain contain 2 elements") - } -} diff --git a/libs/test/matcher.go b/libs/test/matcher.go deleted file mode 100644 index 44a799c82..000000000 --- a/libs/test/matcher.go +++ /dev/null @@ -1,28 +0,0 @@ -package test - -import ( - "fmt" - - "github.com/golang/mock/gomock" - "github.com/shopspring/decimal" -) - -// DecEq returns a matcher that matches a decimal.Decimal of equal value. -func DecEq(x decimal.Decimal) gomock.Matcher { return decMatcher{x} } - -type decMatcher struct { - x decimal.Decimal -} - -func (e decMatcher) Matches(x interface{}) bool { - switch v := x.(type) { - case decimal.Decimal: - return e.x.Equals(v) - default: - return false - } -} - -func (e decMatcher) String() string { - return fmt.Sprintf("is equal to %v", e.x) -} diff --git a/libs/test/random.go b/libs/test/random.go deleted file mode 100644 index 194d8f499..000000000 --- a/libs/test/random.go +++ /dev/null @@ -1,45 +0,0 @@ -// Package test provides utilities for testing. Do not import this into non-test code. -package test - -import ( - "crypto/rand" - "math" - "math/big" -) - -// RandomString return a random alphanumeric string with length 10. -func RandomString() string { - return RandomStringWithLen(10) -} - -// RandomStringWithLen returns a random alphanumeric string with a specified length. -func RandomStringWithLen(length int) string { - var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - s := make([]rune, length) - for i := range s { - n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) - s[i] = letters[n.Int64()] - } - return string(s) -} - -// RandomInt return a random int up to math.MaxInt32. -func RandomInt() int { - return RandomIntWithMax(math.MaxInt32) -} - -// RandomIntWithMax returns a random int in range [0, max]. -func RandomIntWithMax(max int) int { - n, _ := rand.Int(rand.Reader, big.NewInt(int64(max))) - i := n.Int64() - if i == 0 { - i = 1 - } - return int(i) -} - -// RandomNonZeroInt return a random nonzero int up to the supplied max. -func RandomNonZeroInt(max int) int { - n, _ := rand.Int(rand.Reader, big.NewInt(int64(max)-1)) - return int(n.Int64() + 1) -} diff --git a/libs/time/duration.go b/libs/time/duration.go deleted file mode 100644 index b22138a5b..000000000 --- a/libs/time/duration.go +++ /dev/null @@ -1,171 +0,0 @@ -// Package time parses RFC3339 duration strings into time.Duration -// taken from https://github.com/peterhellberg/duration -package time - -import ( - "fmt" - "regexp" - "strconv" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/contains" -) - -// oneMonth := ISODuration("P1M") -// t, err := oneMonth.InFuture() - -// ISODuration - iso representation of -type ISODuration string - -// String - implement stringer -func (i *ISODuration) String() string { - return string(*i) -} - -// ParseDuration a RFC3339 duration string into time.Duration -func ParseDuration(s string) (*ISODuration, error) { - if contains.Str(invalidStrings, s) || strings.HasSuffix(s, "T") { - return nil, ErrInvalidString - } - if !pattern.MatchString(s) { - return nil, ErrUnsupportedFormat - } - d := ISODuration(s) - return &d, nil -} - -// FromNow - add this isoduration to time.Now, resulting in a new time -func (i *ISODuration) FromNow() (*time.Time, error) { - t, err := i.From(time.Now()) - if err != nil { - return nil, fmt.Errorf("failed to add duration to now: %w", err) - } - return t, nil -} - -// From - return a time relative to a given time based on the ISODuration -func (i *ISODuration) From(t time.Time) (*time.Time, error) { - d, err := i.base(t) - if err != nil { - return nil, fmt.Errorf("failed to add duration to now: %w", err) - } - tt := t.Add(d) - return &tt, nil -} - -const ( - // HoursPerDay is the number of hours per day according to Google - HoursPerDay = 24.0 - - // HoursPerWeek is the number of hours per week according to Google - HoursPerWeek = 168.0 - - // HoursPerMonth is the number of hours per month according to Google - HoursPerMonth = 730.4841667 - - // HoursPerYear is the number of hours per year according to Google - HoursPerYear = 8765.81 -) - -var ( - // ErrInvalidString is returned when passed an invalid string - ErrInvalidString = fmt.Errorf("invalid duration string") - - // ErrUnsupportedFormat is returned when parsing fails - ErrUnsupportedFormat = fmt.Errorf("unsupported duration string format") - - pattern = regexp.MustCompile(`\A(-)?P((?P[\d\.]+)Y)?((?P[\d\.]+)M)?((?P[\d\.]+)W)?((?P[\d\.]+)D)?(T((?P[\d\.]+)H)?((?P[\d\.]+)M)?((?P[\d\.]+?)S)?)?\z`) - - invalidStrings = []string{"", "P", "PT"} -) - -// base - given a base, produce a time.Duration from base for the ISODuration -func (i *ISODuration) base(t time.Time) (time.Duration, error) { - if i == nil { - return 0, nil - } - s := i.String() - - var ( - match []string - prefix string - ) - - if pattern.MatchString(s) { - match = pattern.FindStringSubmatch(s) - } else { - return 0, ErrUnsupportedFormat - } - - if strings.HasPrefix(s, "-") { - prefix = "-" - } - - return durationFromMatchAndPrefix(match, prefix) -} - -func durationFunc(prefix string) func(string, float64) time.Duration { - return func(format string, f float64) time.Duration { - if d, err := time.ParseDuration(fmt.Sprintf(format, f)); err == nil { - return d - } - - return time.Duration(0) - } -} - -func durationFromMatchAndPrefix(match []string, prefix string) (time.Duration, error) { - d := time.Duration(0) - - duration := durationFunc(prefix) - - for i, name := range pattern.SubexpNames() { - value := match[i] - if i == 0 || name == "" || value == "" { - continue - } - - if f, err := strconv.ParseFloat(prefix+value, 64); err == nil { - n := time.Now() - rem := f - float64(int(f)) - switch name { - case "years": - // get actual duration (relative to now) - d += n.AddDate(int(f), 0, 0).Sub(n) - if rem > 0 { - d += duration("%fh", rem*HoursPerYear) - } - case "months": - // get actual duration (relative to now) - d += n.AddDate(0, int(f), 0).Sub(n) - if rem > 0 { - d += duration("%fh", rem*HoursPerMonth) - } - //d += duration("%fh", f*HoursPerMonth) - case "weeks": - // get actual duration (relative to now) - d += n.AddDate(0, 0, int(f)*7).Sub(n) - if rem > 0 { - d += duration("%fh", rem*HoursPerWeek) - } - //d += duration("%fh", f*HoursPerWeek) - case "days": - // get actual duration (relative to now) - d += n.AddDate(0, 0, int(f)).Sub(n) - //d += duration("%fh", f*HoursPerDay) - if rem > 0 { - d += duration("%fh", (f-float64(int(f)))*HoursPerDay) - } - case "hours": - d += duration("%fh", f) - case "minutes": - d += duration("%fm", f) - case "seconds": - d += duration("%fs", f) - } - } - } - - return d, nil -} diff --git a/libs/time/duration_test.go b/libs/time/duration_test.go deleted file mode 100644 index c01cb3ee4..000000000 --- a/libs/time/duration_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package time_test - -import ( - "testing" - "time" - - timeutils "github.com/brave-intl/bat-go/libs/time" -) - -func TestParse(t *testing.T) { - for i, tt := range []struct { - dur string - err error - out float64 - }{ - {"PT1.5M", nil, 90}, - {"PT0.5H", nil, 1800}, - {"PT0.5H29M60S", nil, 3600}, // Probably shouldn’t be valid since only the last value can have fractions - {"PT15S", nil, 15}, - {"PT1M", nil, 60}, - {"PT3M", nil, 180}, - {"PT130S", nil, 130}, - {"PT2M10S", nil, 130}, - {"P1DT2S", nil, 86402}, - {"PT5M10S", nil, 310}, - {"PT1H30M5S", nil, 5405}, - {"P2DT1H10S", nil, 176410}, - {"PT1004199059S", nil, 1004199059}, - {"P3DT5H20M30.123S", nil, 278430.123}, - {"P1W", nil, 604800}, - {"P0.123W", nil, 74390.4}, - {"P1WT5S", nil, 604805}, - {"P1WT1H", nil, 608400}, - //{"P2YT1H30M5S", nil, 63119237}, // constants do not align with new now base - //{"P1Y2M3DT5H20M30.123S", nil, 37094832.1218}, // constants do not align with new now base - //{"-P1Y2M3DT5H20M30.123S", nil, -37094832.1218}, // constants do not align with new now base - {"-P1WT1H", nil, -608400}, - {"-P1DT2S", nil, -86402}, - {"-PT1M5S", nil, -65}, - //{"-P0.123W", nil, -74390.4}, // constants do not align with new now base - - // Not supported since fields in the wrong order - {"P1M2Y", timeutils.ErrUnsupportedFormat, 0}, - - // Not supported since negative value - {"P-1Y", timeutils.ErrUnsupportedFormat, 0}, - - // Not supported since negative value - {"P1YT-1M", timeutils.ErrUnsupportedFormat, 0}, - - // Not supported since missing T - {"P1S", timeutils.ErrUnsupportedFormat, 0}, - - // Not supported since missing P - {"1Y", timeutils.ErrUnsupportedFormat, 0}, - - // Not supported since no value is specified for months - {"P1YM5D", timeutils.ErrUnsupportedFormat, 0}, - - // Not supported since wrong format of string - {"FOOBAR", timeutils.ErrUnsupportedFormat, 0}, - - // Invalid since empty string - {"", timeutils.ErrInvalidString, 0}, - - // Invalid since no time fields present - {"P", timeutils.ErrInvalidString, 0}, - - // Invalid since no time fields present - {"PT", timeutils.ErrInvalidString, 0}, - - // Invalid since ending with T - {"P1Y2M3DT", timeutils.ErrInvalidString, 0}, - } { - - id, err := timeutils.ParseDuration(tt.dur) - if err != tt.err { - t.Fatalf("[%d] unexpected error: %s", i, err) - } - - n := time.Now() - - ft, err := id.From(n) - if err != nil { - t.Fatalf("[%d] unexpected error: %s", i, err) - } - - d := (*ft).Sub(n) - - if got := d.Seconds(); got != tt.out { - t.Errorf("[%d] Parse(%q) -> d.Seconds() = %f, want %f", i, tt.dur, got, tt.out) - } - } -} - -func TestCompareWithTimeParseDuration(t *testing.T) { - for i, tt := range []struct { - timeStr string - durationStr string - }{ - {"1h", "PT1H"}, - {"9m60s", "PT10.0M"}, - {"1h2m", "PT1H2M"}, - {"2h15s", "PT1H60M15S"}, - {"169h", "P1WT1H"}, - } { - td, _ := time.ParseDuration(tt.timeStr) - - id, err := timeutils.ParseDuration(tt.durationStr) - if err != nil { - t.Fatalf("[%d] unexpected error: %s", i, err) - } - - n := time.Now() - - ft, err := id.From(n) - if err != nil { - t.Fatalf("[%d] unexpected error: %s", i, err) - } - - dd := (*ft).Sub(n) - - if td != dd { - t.Errorf(`[%d] not equal: %q->%v != %q->%v`, i, tt.timeStr, td, tt.durationStr, dd) - } - } -} diff --git a/libs/useragent/useragent.go b/libs/useragent/useragent.go deleted file mode 100644 index ff9fcf9d9..000000000 --- a/libs/useragent/useragent.go +++ /dev/null @@ -1,35 +0,0 @@ -package useragent - -import ( - "strings" - - "github.com/mssola/user_agent" -) - -var ( - checks = [][]string{ - {"iphone", "ios"}, - {"android", "android"}, - {"windows", "windows"}, - {"mac os x", "osx"}, - {"linux", "linux"}, - } -) - -// ParsePlatform parses a platform known to grants from ua -func ParsePlatform(ua string) string { - if ua == "" { - return "" - } - parsed := user_agent.New(ua) - if parsed == nil { - return "" - } - os := strings.ToLower(parsed.OS()) - for _, check := range checks { - if strings.Contains(os, check[0]) { - return check[1] - } - } - return "" -} diff --git a/libs/useragent/useragent_test.go b/libs/useragent/useragent_test.go deleted file mode 100644 index 2d5663dee..000000000 --- a/libs/useragent/useragent_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package useragent - -import ( - "testing" -) - -func TestParsePlatform(t *testing.T) { - situations := [][]string{ - {"osx", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"}, - {"ios", "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1"}, - {"android", "Mozilla/5.0 (Linux; Android 10; Pixel 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"}, - {"linux", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"}, - {"windows", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"}, - } - for _, situation := range situations { - if ParsePlatform(situation[1]) != situation[0] { - t.Errorf("incorrect match: %s", situation[0]) - } - } -} diff --git a/libs/validators/validators.go b/libs/validators/validators.go deleted file mode 100644 index b02e1231d..000000000 --- a/libs/validators/validators.go +++ /dev/null @@ -1,99 +0,0 @@ -package validators - -import ( - "regexp" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/altcurrency" - uuid "github.com/satori/go.uuid" -) - -func init() { - govalidator.TagMap["base64url"] = govalidator.Validator(IsBase64Url) - govalidator.TagMap["base64urlnopad"] = govalidator.Validator(IsBase64UrlWithoutPadding) - govalidator.TagMap["compactjws"] = govalidator.Validator(IsCompactJWS) - govalidator.TagMap["btcaddress"] = govalidator.Validator(IsBTCAddress) - govalidator.TagMap["ethaddressnochecksum"] = govalidator.Validator(IsETHAddressNoChecksum) - govalidator.TagMap["ethaddress"] = govalidator.Validator(IsETHAddress) - govalidator.TagMap["platform"] = govalidator.Validator(IsPlatform) - govalidator.CustomTypeTagMap.Set("requiredUUID", govalidator.CustomTypeValidator(IsRequiredUUID)) - -} - -const ( - base64Url string = "^(?:[A-Za-z0-9+_-]{4})*(?:[A-Za-z0-9+_-]{2}==|[A-Za-z0-9+_-]{3}=|[A-Za-z0-9+_-]{4})$" - base64UrlNoPad string = "^[A-Za-z0-9+_-]+$" - compactJWS string = "^[A-Za-z0-9+_-]+[.][A-Za-z0-9+_-]+[.][A-Za-z0-9+_-]+$" - btcAddress string = "^[a-zA-Z1-9]{27,35}$" - ethAddress string = "^0x[0-9a-fA-F]{40}$" -) - -var ( - rxBase64Url = regexp.MustCompile(base64Url) - rxBase64UrlNoPad = regexp.MustCompile(base64UrlNoPad) - rxCompactJWS = regexp.MustCompile(compactJWS) - rxBTCAddress = regexp.MustCompile(btcAddress) - rxETHAddress = regexp.MustCompile(ethAddress) -) - -// IsBase64Url returns true if the string str is base64url (encoded with the "URL and Filename safe" alphabet) -// https://tools.ietf.org/html/rfc4648#section-5 -func IsBase64Url(str string) bool { - return rxBase64Url.MatchString(str) -} - -// IsBase64UrlWithoutPadding returns true if the string str is base64url encoded with end padding omitted -func IsBase64UrlWithoutPadding(str string) bool { - return rxBase64UrlNoPad.MatchString(str) -} - -// IsCompactJWS returns true if the string str is a JSW in the compact JSON serialization -func IsCompactJWS(str string) bool { - return rxCompactJWS.MatchString(str) -} - -// IsBTCAddress returns true if the string str is a bitcoin address -func IsBTCAddress(str string) bool { - if !rxBTCAddress.MatchString(str) { - return false - } - if altcurrency.GetBTCAddressVersion(str) < 0 { - return false - } - return true -} - -// IsETHAddressNoChecksum returns true if the string str is a ethereum address -func IsETHAddressNoChecksum(str string) bool { - return rxETHAddress.MatchString(str) -} - -// IsETHAddress returns true if the string str is a ethereum address -func IsETHAddress(str string) bool { - if !IsETHAddressNoChecksum(str) { - return false - } - return altcurrency.ToChecksumETHAddress(str) == str -} - -// IsPlatform determines whether or not a given string is a recognized platform -func IsPlatform(platform string) bool { - platforms := []string{"ios", "android", "osx", "windows", "linux", "desktop"} - return govalidator.IsIn(platform, platforms...) -} - -// IsRequiredUUID checks if the uuid is present -func IsRequiredUUID(i interface{}, context interface{}) bool { - switch v := i.(type) { // you can type switch on the context interface being validated - case uuid.UUID: - return !uuid.Equal(v, uuid.Nil) - default: - panic("invalid type recieved in IsRequiredUUID") - } -} - -// IsUUID checks if the string is a valid UUID -func IsUUID(v string) bool { - _, err := uuid.FromString(v) - return err == nil -} diff --git a/libs/validators/validators_test.go b/libs/validators/validators_test.go deleted file mode 100644 index 328a03f7f..000000000 --- a/libs/validators/validators_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package validators - -import ( - "testing" - - "github.com/asaskevich/govalidator" - uuid "github.com/satori/go.uuid" -) - -func TestIsBase64Url(t *testing.T) { - if !IsBase64Url("eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9") { - t.Error("Unexpected error on valid base64url encoded string") - } - if IsBase64Url("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk==") { - t.Error("Unexpected error on valid base64url encoded string with padding") - } - if IsBase64Url("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") { - t.Error("Expected error on base64url encoded string missing padding") - } -} - -func TestIsBase64UrlWithoutPadding(t *testing.T) { - if !IsBase64UrlWithoutPadding("eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9") { - t.Error("Unexpected error on valid base64url encoded string") - } - if !IsBase64UrlWithoutPadding("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") { - t.Error("Unexpected error on base64url encoded string missing padding") - } -} - -func TestIsCompactJWS(t *testing.T) { - if !IsCompactJWS("eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") { - t.Error("Unexpected error on valid compact JWS string") - } -} - -func TestIsBTCAddress(t *testing.T) { - if !IsBTCAddress("1HZ8g817ZgfLUCALFnnLPdgEUsmwHLb73W") { - t.Error("Unexpected error on valid BTC address") - } - if IsBTCAddress("FHZ8g817ZgfLUCALFnnLPdgEUsmwHLb73W") { - t.Error("Expected error on valid BTC address") - } -} - -func TestIsETHAddress(t *testing.T) { - if !IsETHAddress("0xF1A61415e12DB93ABACE8704855A4795934ff992") { - t.Error("Unexpected error on valid ETH address") - } - if IsETHAddress("0xf1a61415e12db93abace8704855a4795934ff992") { - t.Error("Expected error on ETH address missing checksum") - } - if IsETHAddress("0xF1A61415e12DB93ABACE8704855A4795934FF992") { - t.Error("Unexpected error on ETH address with invalid checksum") - } -} - -func TestIsPlatform(t *testing.T) { - if IsPlatform("notaplatform") { - t.Error("non platforms should not pass") - } - if IsPlatform("") { - t.Error("empty strings do not pass") - } - if !IsPlatform("osx") { - t.Error("strings in the list should pass") - } -} - -func TestIsUUID(t *testing.T) { - if IsUUID("notauuid") { - t.Error("non uuids should not pass") - } - if IsUUID("") { - t.Error("empty strings do not pass") - } - if !IsUUID("01e42e30-a823-4a91-a114-00fd0d47f7d0") { - t.Error("a uuid should not fail") - } - if !IsUUID("424aab2c-3b95-5e7e-9ec3-1ca9349f5887") { - t.Error("a uuid should not fail") - } -} - -func TestIsEmptyUUID(t *testing.T) { - type TestRequest struct { - ID uuid.UUID `valid:"requiredUUID"` - } - - request := &TestRequest{uuid.FromStringOrNil("01e42e30-a823-4a91-a114-00fd0d47f7d0")} - - isValid, err := govalidator.ValidateStruct(request) - if err != nil { - t.Error("should not error") - } - if !isValid { - t.Error("should be valid uuid") - } - - request.ID = uuid.Nil - - isValid, err = govalidator.ValidateStruct(request) - if err == nil { - t.Error("should error", err) - } - if isValid { - t.Error("should not be a valid uuid") - } - -} diff --git a/libs/wallet/provider/provider.go b/libs/wallet/provider/provider.go deleted file mode 100644 index 08fe722f0..000000000 --- a/libs/wallet/provider/provider.go +++ /dev/null @@ -1,25 +0,0 @@ -package provider - -import ( - "context" - "fmt" - - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" -) - -// GetWallet returns the wallet corresponding to the passed wallet info -func GetWallet(ctx context.Context, info wallet.Info) (wallet.Wallet, error) { - switch info.Provider { - case "uphold": - // anon card case - uW, err := uphold.FromWalletInfo(ctx, info) - if err != nil { - return uW, err - } - // TODO once we can retrieve public key info from uphold - // err = uW.UpdatePublicKey() - return uW, err - } - return nil, fmt.Errorf("no such supported wallet provider %s", info.Provider) -} diff --git a/libs/wallet/provider/uphold/errors.go b/libs/wallet/provider/uphold/errors.go deleted file mode 100644 index 25d3c2c91..000000000 --- a/libs/wallet/provider/uphold/errors.go +++ /dev/null @@ -1,141 +0,0 @@ -package uphold - -import ( - "encoding/json" - "fmt" - "strings" -) - -// DrainData - uphold specific drain error "data" wrapper for errorutils -type DrainData struct { - code string -} - -// NewDrainData - get uphold specific drain data from the coded error -func NewDrainData(c Coded) *DrainData { - return &DrainData{ - code: strings.ToLower(c.GetCode()), - } -} - -// DrainCode - implement the drain code rendering of the error -func (dd *DrainData) DrainCode() (string, bool) { - return fmt.Sprintf("uphold_%s", dd.code), true -} - -// Coded - interface for things that have codes, such as errors -type Coded interface { - GetCode() string -} - -type upholdBaseError struct { - Code string `json:"code"` - Message string `json:"message"` -} - -// TODO just use json.RawMessage - -type upholdDenominationValidationErrors struct { - AmountError []upholdBaseError `json:"amount,omitempty"` - Data json.RawMessage `json:",omitempty"` -} - -type upholdDenominationErrors struct { - Code string `json:"code"` - ValidationErrors upholdDenominationValidationErrors `json:"errors,omitempty"` - Data json.RawMessage `json:",omitempty"` -} - -type upholdValidationErrors struct { - SignatureError []upholdBaseError `json:"signature,omitempty"` - DenominationErrors upholdDenominationErrors `json:"denomination,omitempty"` - DestinationErrors []upholdBaseError `json:"destination,omitempty"` - Data json.RawMessage `json:",omitempty"` -} - -type upholdError struct { - Message string `json:"error,omitempty"` - Code string `json:"code"` - Restrictions []string `json:"restrictions,omitempty"` - ValidationErrors upholdValidationErrors `json:"errors,omitempty"` - Data json.RawMessage `json:",omitempty"` - RequestID string `json:"requestId,omitempty"` -} - -// Code - implement coded interface -func (uhErr upholdError) GetCode() string { - // forbidden case needs to append restrictions - code := uhErr.Code - if uhErr.Restrictions != nil && len(uhErr.Restrictions) > 0 { - code = fmt.Sprintf("%s_%s", code, uhErr.Restrictions[0]) - } - return code -} - -func (uhErr upholdError) NotFoundError() bool { - return uhErr.Code == "not_found" -} - -func (uhErr upholdError) ValidationError() bool { - return uhErr.Code == "validation_failed" -} - -func (uhErr upholdError) AlreadyExistsError() bool { - return uhErr.Code == "transaction_already_exists" -} - -func (uhErr upholdError) DenominationError() bool { - return uhErr.ValidationError() && uhErr.ValidationErrors.DenominationErrors.Code == "validation_failed" -} - -func (uhErr upholdError) DestinationError() bool { - return uhErr.ValidationError() && len(uhErr.ValidationErrors.DestinationErrors) > 0 -} - -func (uhErr upholdError) InvalidDestination() bool { - return uhErr.DestinationError() -} - -func (uhErr upholdError) AmountError() bool { - return uhErr.DenominationError() && len(uhErr.ValidationErrors.DenominationErrors.ValidationErrors.AmountError) > 0 -} - -func (uhErr upholdError) InsufficientBalance() bool { - if uhErr.AmountError() { - for _, ae := range uhErr.ValidationErrors.DenominationErrors.ValidationErrors.AmountError { - if ae.Code == "sufficient_funds" { - return true - } - } - } - return false -} - -func (uhErr upholdError) InvalidSignature() bool { - return uhErr.ValidationError() && len(uhErr.ValidationErrors.SignatureError) > 0 -} - -func (uhErr upholdError) ForbiddenError() bool { - return uhErr.Code == "forbidden" -} - -func (uhErr upholdError) String() string { - if uhErr.InsufficientBalance() { - for _, ae := range uhErr.ValidationErrors.DenominationErrors.ValidationErrors.AmountError { - if ae.Code == "sufficient_funds" { - return ae.Message - } - } - } else if uhErr.InvalidSignature() { - return "Signature: " + uhErr.ValidationErrors.SignatureError[0].Message - } - b, err := json.Marshal(&uhErr) - if err != nil { - panic(err) - } - return string(b) -} - -func (uhErr upholdError) Error() string { - return "UpholdError: " + uhErr.String() -} diff --git a/libs/wallet/provider/uphold/errors_test.go b/libs/wallet/provider/uphold/errors_test.go deleted file mode 100644 index cd8a01013..000000000 --- a/libs/wallet/provider/uphold/errors_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package uphold - -import ( - "encoding/json" - "testing" -) - -func TestForbiddenRestriction(t *testing.T) { - errJSON := []byte(`{"code":"forbidden","restrictions":["user-cannot-recieve-funds"]}`) - var uhErr upholdError - err := json.Unmarshal(errJSON, &uhErr) - if err != nil { - t.Error("Unexpected error during uphold error unmarshal") - } - - if !uhErr.ForbiddenError() { - t.Error("Expected resulting error to be for forbidden") - } - // check codified drain error is right - dc := NewDrainData(uhErr) - if code, _ := dc.DrainCode(); code != "uphold_forbidden_user-cannot-recieve-funds" { - t.Error("invalid resulting user drain code") - } -} - -func TestInsufficientBalance(t *testing.T) { - errJSON := []byte(`{"code":"validation_failed","errors":{"denomination":{"code":"validation_failed","errors":{"amount":[{"code":"sufficient_funds","message":"Not enough funds for the specified amount"}]}}}}`) - var uhErr upholdError - err := json.Unmarshal(errJSON, &uhErr) - if err != nil { - t.Error("Unexpected error during uphold error unmarshal") - } - - if !uhErr.InsufficientBalance() { - t.Error("Expected resulting error to be for insufficient balance") - } - if uhErr.InvalidSignature() { - t.Error("Expected resulting error to only be for insufficient balance") - } - if uhErr.Error() != "UpholdError: Not enough funds for the specified amount" { - t.Error("Incorrect resulting error string") - } -} - -func TestInvalidSignature(t *testing.T) { - errJSON := []byte(`{"code":"validation_failed","errors":{"signature":[{"code":"required","message":"This value is required"}]}}`) - var uhErr upholdError - err := json.Unmarshal(errJSON, &uhErr) - if err != nil { - t.Error("Unexpected error during uphold error unmarshal") - } - - if !uhErr.InvalidSignature() { - t.Error("Expected resulting error to be for invalid signature") - } - if uhErr.InsufficientBalance() { - t.Error("Expected resulting error to only be for invalid signature") - } - if uhErr.Error() != "UpholdError: Signature: This value is required" { - t.Error("Incorrect resulting error string") - } - - errJSON = []byte(`{"code":"validation_failed","errors":{"signature":[{"code":"invalid","message":"This value is not valid"}]}}`) - uhErr = upholdError{} - err = json.Unmarshal(errJSON, &uhErr) - if err != nil { - t.Error("Unexpected error during uphold error unmarshal") - } - - if !uhErr.InvalidSignature() { - t.Error("Expected resulting error to be for invalid signature") - } - if uhErr.InsufficientBalance() { - t.Error("Expected resulting error to only be for invalid signature") - } - if uhErr.Error() != "UpholdError: Signature: This value is not valid" { - t.Error("Incorrect resulting error string") - } -} diff --git a/libs/wallet/provider/uphold/uphold.go b/libs/wallet/provider/uphold/uphold.go deleted file mode 100644 index 94d5a1991..000000000 --- a/libs/wallet/provider/uphold/uphold.go +++ /dev/null @@ -1,1125 +0,0 @@ -package uphold - -import ( - "bytes" - "context" - "crypto" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "net" - "net/http" - "net/http/httputil" - "net/url" - "os" - "strconv" - "strings" - "time" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/digest" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/libs/pindialer" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/brave-intl/bat-go/libs/validators" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "golang.org/x/crypto/ed25519" -) - -// Wallet a wallet information using Uphold as the provider -// A wallet corresponds to a single Uphold "card" -type Wallet struct { - walletutils.Info - Logger *zerolog.Logger - PrivKey crypto.Signer - PubKey httpsignature.Verifier -} - -const ( - dateFormat = "2006-01-02T15:04:05.000Z" - batchSize = 50 - listTransactionsRetries = 5 -) - -const ( - // The Intermediate Certificates - sandboxFingerprint = "UDZHf1C3qefyDm62At7xuhruZquvumx3gDc8dgeeki4=" - prodFingerprint = "UDZHf1C3qefyDm62At7xuhruZquvumx3gDc8dgeeki4=" -) - -var ( - // SettlementDestination is the address of the settlement wallet - SettlementDestination = os.Getenv("BAT_SETTLEMENT_ADDRESS") - - // AnonCardSettlementAddress is the address of the settlement wallet - AnonCardSettlementAddress = os.Getenv("ANON_CARD_SETTLEMENT_ADDRESS") - // UpholdSettlementAddress is the address of the settlement wallet - UpholdSettlementAddress = os.Getenv("UPHOLD_SETTLEMENT_ADDRESS") - - grantWalletCardID = os.Getenv("GRANT_WALLET_CARD_ID") - grantWalletPrivateKey = os.Getenv("GRANT_WALLET_PRIVATE_KEY") - grantWalletPublicKey = os.Getenv("GRANT_WALLET_PUBLIC_KEY") - - personalAccessToken = os.Getenv("UPHOLD_ACCESS_TOKEN") - clientCredentialsToken = os.Getenv("UPHOLD_CLIENT_CREDENTIALS_TOKEN") - environment = os.Getenv("UPHOLD_ENVIRONMENT") - upholdProxy = os.Getenv("UPHOLD_HTTP_PROXY") - upholdAPIBase = map[string]string{ - "": "https://api-sandbox.uphold.com", // os.Getenv() will return empty string if not set - "test": "https://mock.uphold.com", - "sandbox": "https://api-sandbox.uphold.com", - "prod": "https://api.uphold.com", - }[environment] - upholdCertFingerprint = map[string]string{ - "": sandboxFingerprint, // os.Getenv() will return empty string if not set - "sandbox": sandboxFingerprint, - "prod": prodFingerprint, - }[environment] - client *http.Client -) - -func init() { - prometheus.MustRegister(countUpholdWalletAccountValidation) - prometheus.MustRegister(countUpholdTxDestinationGeo) - - // Default back to BAT_SETTLEMENT_ADDRESS - if AnonCardSettlementAddress == "" { - AnonCardSettlementAddress = SettlementDestination - } - if UpholdSettlementAddress == "" { - UpholdSettlementAddress = SettlementDestination - } - - var proxy func(*http.Request) (*url.URL, error) - if len(upholdProxy) > 0 { - proxyURL, err := url.Parse(upholdProxy) - if err != nil { - panic("UPHOLD_HTTP_PROXY is not a valid proxy URL") - } - proxy = http.ProxyURL(proxyURL) - } else { - proxy = nil - } - client = &http.Client{ - Timeout: time.Second * 60, - Transport: middleware.InstrumentRoundTripper( - &http.Transport{ - Proxy: proxy, - DialTLSContext: pindialer.MakeContextDialer(upholdCertFingerprint), - }, "uphold"), - } -} - -// New returns an uphold wallet constructed using the provided parameters -// NOTE that it does not register a wallet with Uphold if it does not already exist -func New(ctx context.Context, info walletutils.Info, privKey crypto.Signer, pubKey httpsignature.Verifier) (*Wallet, error) { - if info.Provider != "uphold" { - return nil, errors.New("the wallet provider or deposit account must be uphold") - } - if len(info.ProviderID) > 0 { - if !validators.IsUUID(info.ProviderID) { - return nil, errors.New("an uphold cardId (the providerId) must be a UUIDv4") - } - } else { - return nil, errors.New("generation of new uphold wallet is not yet implemented") - } - if !info.AltCurrency.IsValid() { - return nil, errors.New("a wallet must have a valid altcurrency") - } - return &Wallet{Info: info, PrivKey: privKey, PubKey: pubKey}, nil -} - -// FromWalletInfo returns an uphold wallet matching the provided wallet info -func FromWalletInfo(ctx context.Context, info walletutils.Info) (*Wallet, error) { - var publicKey httpsignature.Ed25519PubKey - if len(info.PublicKey) > 0 { - var err error - publicKey, err = hex.DecodeString(info.PublicKey) - if err != nil { - return nil, err - } - } - return New(ctx, info, ed25519.PrivateKey{}, publicKey) -} - -func newRequest(method, path string, body io.Reader) (*http.Request, error) { - req, err := http.NewRequest(method, upholdAPIBase+path, body) - if err == nil { - if len(clientCredentialsToken) > 0 { - req.Header.Add("Authorization", "Bearer "+clientCredentialsToken) - } else { - req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(personalAccessToken+":X-OAuth-Basic"))) - } - } - return req, err -} - -func submit(logger *zerolog.Logger, req *http.Request) ([]byte, *http.Response, error) { - req.Header.Add("content-type", "application/json") - - dump, err := httputil.DumpRequestOut(req, true) - if err != nil { - panic(err) - } - dump = clients.RedactSensitiveHeaders(dump) - - if logger != nil { - logger.Debug(). - Str("path", "github.com/brave-intl/bat-go/wallet/provider/uphold"). - Str("type", "http.Request"). - Msg(string(dump)) - } - - resp, err := client.Do(req) - if err != nil { - return nil, resp, fmt.Errorf("%w: %s", errorutils.ErrFailedClientRequest, err.Error()) - } - - headers := map[string][]string(resp.Header) - jsonHeaders, err := json.MarshalIndent(headers, "", " ") - if err != nil { - return nil, resp, err - } - - body, err := requestutils.Read(logger.WithContext(context.Background()), resp.Body) - if err != nil { - return nil, resp, fmt.Errorf("%w: %s", errorutils.ErrFailedBodyRead, err.Error()) - } - - if logger != nil { - logger.Debug(). - Str("path", "github.com/brave-intl/bat-go/wallet/provider/uphold"). - Str("type", "http.Response"). - Int("status", resp.StatusCode). - Str("headers", string(jsonHeaders)). - Msg(string(body)) - } - - if resp.StatusCode/100 != 2 { - var uhErr upholdError - if json.Unmarshal(body, &uhErr) != nil { - return nil, resp, fmt.Errorf("Error %d, %s", resp.StatusCode, body) - } - uhErr.RequestID = resp.Header.Get("Request-Id") - return nil, resp, uhErr - } - return body, resp, nil -} - -type createCardRequest struct { - Label string `json:"label"` - AltCurrency *altcurrency.AltCurrency `json:"currency"` - PublicKey string `json:"publicKey"` -} - -// IsUserKYC - is this user a "member" -func (w *Wallet) IsUserKYC(ctx context.Context, destination string) (string, bool, string, error) { - logger := logging.FromContext(ctx) - - // in order to get the isMember status of the wallet, we need to start - // a transaction of 0 BAT to the wallet "w" from "grant_wallet" but never commit - - gwPublicKey, err := hex.DecodeString(grantWalletPublicKey) - if err != nil { - logger.Error().Err(err).Msg("invalid system public key") - return "", false, "", fmt.Errorf("invalid system public key: %w", err) - } - gwPrivateKey, err := hex.DecodeString(grantWalletPrivateKey) - if err != nil { - logger.Error().Err(err).Msg("invalid system private key") - return "", false, "", fmt.Errorf("invalid system private key: %w", err) - } - - grantWallet := Wallet{ - Info: walletutils.Info{ - ProviderID: grantWalletCardID, - Provider: "uphold", - PublicKey: grantWalletPublicKey, - }, - PrivKey: ed25519.PrivateKey([]byte(gwPrivateKey)), - PubKey: httpsignature.Ed25519PubKey([]byte(gwPublicKey)), - } - - // prepare a transaction by creating a payload - transactionB64, err := grantWallet.PrepareTransaction(altcurrency.BAT, decimal.New(0, 1), destination, "", "", nil) - if err != nil { - logger.Error().Err(err).Msg("failed to prepare transaction") - return "", false, "", fmt.Errorf("failed to prepare transaction: %w", err) - } - - // submit the transaction the payload - uhResp, err := grantWallet.SubmitTransaction(ctx, transactionB64, false) - if err != nil { - logger.Error().Err(err).Msg("failed to submit transaction") - return "", false, "", fmt.Errorf("failed to submit transaction: %w", err) - } - - if requireCountry, ok := ctx.Value(appctx.RequireUpholdCountryCTXKey).(bool); ok && requireCountry { - // no identity country data from uphold, block the linking attempt - // requires uphold destination country support prior to deploy - if uhResp.IdentityCountry == "" { - countUpholdWalletAccountValidation.With(prometheus.Labels{ - "citizenship_country": uhResp.CitizenshipCountry, - "identity_country": uhResp.IdentityCountry, - "residence_country": uhResp.ResidenceCountry, - "status": "failure", - }).Inc() - return uhResp.UserID, uhResp.KYC, uhResp.IdentityCountry, errorutils.ErrNoIdentityCountry - } - } - - // feature flag for using new custodian regions - if useCustodianRegions, ok := ctx.Value(appctx.UseCustodianRegionsCTXKey).(bool); ok && useCustodianRegions { - // get the uphold custodian supported regions - if custodianRegions, ok := ctx.Value(appctx.CustodianRegionsCTXKey).(*custodian.Regions); ok { - allowed := custodianRegions.Uphold.Verdict( - uhResp.IdentityCountry, - ) - - if !allowed { - countUpholdWalletAccountValidation.With(prometheus.Labels{ - "citizenship_country": uhResp.CitizenshipCountry, - "identity_country": uhResp.IdentityCountry, - "residence_country": uhResp.ResidenceCountry, - "status": "failure", - }).Inc() - return uhResp.UserID, uhResp.KYC, uhResp.IdentityCountry, errorutils.ErrInvalidCountry - } - } - } else { // use default blacklist functionality - // do country blacklist checking - if blacklist, ok := ctx.Value(appctx.BlacklistedCountryCodesCTXKey).([]string); ok { - // check all three country codes to see if any are equal to a blacklist item - for _, v := range blacklist { - if strings.EqualFold(uhResp.IdentityCountry, v) || - strings.EqualFold(uhResp.CitizenshipCountry, v) || - strings.EqualFold(uhResp.ResidenceCountry, v) { - countUpholdWalletAccountValidation.With(prometheus.Labels{ - "citizenship_country": uhResp.CitizenshipCountry, - "identity_country": uhResp.IdentityCountry, - "residence_country": uhResp.ResidenceCountry, - "status": "failure", - }).Inc() - return uhResp.UserID, uhResp.KYC, uhResp.IdentityCountry, errorutils.ErrInvalidCountry - } - } - } - } - countUpholdWalletAccountValidation.With(prometheus.Labels{ - "citizenship_country": uhResp.CitizenshipCountry, - "identity_country": uhResp.IdentityCountry, - "residence_country": uhResp.ResidenceCountry, - "status": "success", - }).Inc() - - return uhResp.UserID, uhResp.KYC, uhResp.IdentityCountry, nil -} - -// sign registration for this wallet with Uphold with label -func (w *Wallet) signRegistration(label string) (*http.Request, error) { - reqPayload := createCardRequest{Label: label, AltCurrency: w.Info.AltCurrency, PublicKey: w.PubKey.String()} - payload, err := json.Marshal(reqPayload) - if err != nil { - return nil, err - } - - req, err := newRequest("POST", "/v0/me/cards", bytes.NewBuffer(payload)) - if err != nil { - return nil, err - } - - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = "primary" - s.Headers = []string{"digest"} - - err = s.Sign(w.PrivKey, crypto.Hash(0), req) - return req, err -} - -// Register a wallet with Uphold with label -func (w *Wallet) Register(ctx context.Context, label string) error { - logger := logging.FromContext(ctx) - - req, err := w.signRegistration(label) - if err != nil { - return err - } - - body, _, err := submit(logger, req) - if err != nil { - return err - } - - var details CardDetails - err = json.Unmarshal(body, &details) - if err != nil { - return err - } - w.Info.ProviderID = details.ID.String() - return nil -} - -// SubmitRegistration from a b64 encoded signed string -func (w *Wallet) SubmitRegistration(ctx context.Context, registrationB64 string) error { - logger := logging.FromContext(ctx) - - b, err := base64.StdEncoding.DecodeString(registrationB64) - if err != nil { - return err - } - - var signedTx httpsignature.HTTPSignedRequest - err = json.Unmarshal(b, &signedTx) - if err != nil { - return err - } - - req, err := newRequest("POST", "/v0/me/cards", nil) - if err != nil { - return err - } - - _, err = signedTx.Extract(req) - if err != nil { - return err - } - - body, _, err := submit(logger, req) - if err != nil { - return err - } - - var details CardDetails - err = json.Unmarshal(body, &details) - if err != nil { - return err - } - w.Info.ProviderID = details.ID.String() - return nil -} - -// PrepareRegistration returns a b64 encoded serialized signed registration suitable for SubmitRegistration -func (w *Wallet) PrepareRegistration(label string) (string, error) { - req, err := w.signRegistration(label) - if err != nil { - return "", err - } - - httpSignedReq, err := httpsignature.EncapsulateRequest(req) - if err != nil { - return "", err - } - - b, err := json.Marshal(&httpSignedReq) - if err != nil { - return "", err - } - - return base64.StdEncoding.EncodeToString(b), nil -} - -// CardSettings contains settings corresponding to the Uphold card -type CardSettings struct { - Protected bool `json:"protected,omitempty"` -} - -// CardDetails contains details corresponding to the Uphold card -type CardDetails struct { - AvailableBalance decimal.Decimal `json:"available"` - Balance decimal.Decimal `json:"balance"` - Currency altcurrency.AltCurrency `json:"currency"` - ID uuid.UUID `json:"id"` - Settings CardSettings `json:"settings"` -} - -// GetCardDetails returns the details associated with the wallet's backing Uphold card -func (w *Wallet) GetCardDetails(ctx context.Context) (*CardDetails, error) { - logger := logging.FromContext(ctx) - - req, err := newRequest("GET", "/v0/me/cards/"+w.ProviderID, nil) - if err != nil { - return nil, err - } - body, _, err := submit(logger, req) - if err != nil { - return nil, err - } - - var details CardDetails - err = json.Unmarshal(body, &details) - if err != nil { - return nil, err - } - return &details, err -} - -// TODO implement func (w *Wallet) UpdatePublicKey() error - -// GetWalletInfo returns the info associated with the wallet -func (w *Wallet) GetWalletInfo() walletutils.Info { - return w.Info -} - -type denomination struct { - Amount decimal.Decimal `json:"amount"` - Currency *altcurrency.AltCurrency `json:"currency"` -} - -// Beneficiary includes information about the recipient of the transaction -type Beneficiary struct { - Address struct { - City string `json:"city,omitempty"` - Country string `json:"country,omitempty"` - Line1 string `json:"line1,omitempty"` - State string `json:"state,omitempty"` - ZipCode string `json:"zipCode,omitempty"` - } `json:"address,omitempty"` - Name string `json:"name,omitempty"` - Relationship string `json:"relationship"` -} - -type transactionRequest struct { - Denomination denomination `json:"denomination"` - Destination string `json:"destination"` - Message string `json:"message,omitempty"` - Purpose string `json:"purpose,omitempty"` - Beneficiary *Beneficiary `json:"beneficiary,omitempty"` -} - -// denominationRecode type was used in this case to maintain trailing zeros so that the validation performed -// on the transaction being checked does not fail -// in order to maintain the zeros, the transaction can be checked using a string -// when using decimal.Decimal, and the transaction is re-serialized the trailing zeros are dropped -type denominationRecode struct { - Amount string `json:"amount"` - Currency *altcurrency.AltCurrency `json:"currency"` -} - -type transactionRequestRecode struct { - Denomination denominationRecode `json:"denomination"` - Destination string `json:"destination"` - Message string `json:"message,omitempty"` - Purpose string `json:"purpose,omitempty"` - Beneficiary *Beneficiary `json:"beneficiary,omitempty"` -} - -func (w *Wallet) signTransfer(altc altcurrency.AltCurrency, probi decimal.Decimal, destination string, message string, purpose string, beneficiary *Beneficiary) (*http.Request, error) { - transferReq := transactionRequest{Denomination: denomination{Amount: altc.FromProbi(probi), Currency: &altc}, Destination: destination, Message: message, Purpose: purpose, Beneficiary: beneficiary} - unsignedTransaction, err := json.Marshal(&transferReq) - if err != nil { - return nil, fmt.Errorf("%w: %s", errorutils.ErrMarshalTransferRequest, err.Error()) - } - - req, err := newRequest("POST", "/v0/me/cards/"+w.ProviderID+"/transactions?commit=true", bytes.NewBuffer(unsignedTransaction)) - if err != nil { - return nil, fmt.Errorf("%w: %s", errorutils.ErrCreateTransferRequest, err.Error()) - } - - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = "primary" - s.Headers = []string{"digest"} - - if err = s.Sign(w.PrivKey, crypto.Hash(0), req); err != nil { - return nil, fmt.Errorf("%w: %s", errorutils.ErrCreateTransferRequest, err.Error()) - } - return req, nil -} - -// PrepareTransaction returns a b64 encoded serialized signed transaction suitable for SubmitTransaction -func (w *Wallet) PrepareTransaction(altcurrency altcurrency.AltCurrency, probi decimal.Decimal, destination string, message string, purpose string, beneficiary *Beneficiary) (string, error) { - req, err := w.signTransfer(altcurrency, probi, destination, message, purpose, beneficiary) - if err != nil { - return "", err - } - - httpSignedReq, err := httpsignature.EncapsulateRequest(req) - if err != nil { - return "", err - } - - b, err := json.Marshal(&httpSignedReq) - if err != nil { - return "", err - } - - return base64.StdEncoding.EncodeToString(b), nil -} - -var ( - countUpholdWalletAccountValidation = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "count_uphold_wallet_account_validation", - Help: "Counts the number of uphold wallets requesting account validation partitioned by country code", - }, - []string{"citizenship_country", "identity_country", "residence_country", "status"}, - ) - countUpholdTxDestinationGeo = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "count_uphold_tx_destination_geo", - Help: "upon transfer record the destination geo information", - }, - []string{"citizenship_country", "identity_country", "residence_country", "type"}, - ) -) - -// Transfer moves funds out of the associated wallet and to the specific destination -func (w *Wallet) Transfer(ctx context.Context, altcurrency altcurrency.AltCurrency, probi decimal.Decimal, destination string) (*walletutils.TransactionInfo, error) { - logger := logging.FromContext(ctx) - - req, err := w.signTransfer(altcurrency, probi, destination, "", "", nil) - if err != nil { - return nil, fmt.Errorf("failed to sign the transfer: %w", err) - } - - respBody, _, err := submit(logger, req) - if err != nil { - // we need this to be draincoded wrapped error so we get the reason for failure in drains - if codedErr, ok := err.(Coded); ok { - return nil, errorutils.New(err, "failed to submit the transfer", NewDrainData(codedErr)) - } - return nil, errorutils.New(err, "failed to submit the transfer", nil) - } - - var uhResp upholdTransactionResponse - err = json.Unmarshal(respBody, &uhResp) - if err != nil { - return nil, fmt.Errorf("%w: %s", errorutils.ErrFailedBodyUnmarshal, err.Error()) - } - - // in the event we have geo information on the transaction report it through metrics - if !( // if there is a destination and all three are not empty strings - uhResp.Destination.Node.User.CitizenshipCountry == "" && - uhResp.Destination.Node.User.IdentityCountry == "" && - uhResp.Destination.Node.User.ResidenceCountry == "") { - var t = "linking" - if !uhResp.Denomination.Amount.IsZero() { - t = "drain" - } - countUpholdTxDestinationGeo.With(prometheus.Labels{ - "citizenship_country": uhResp.Destination.Node.User.CitizenshipCountry, - "identity_country": uhResp.Destination.Node.User.IdentityCountry, - "residence_country": uhResp.Destination.Node.User.ResidenceCountry, - "type": t, - }).Inc() - } - - return uhResp.ToTransactionInfo(), nil -} - -func (w *Wallet) decodeTransaction(transactionB64 string) (*transactionRequest, error) { - b, err := base64.StdEncoding.DecodeString(transactionB64) - if err != nil { - return nil, err - } - - var signedTx httpsignature.HTTPSignedRequest - err = json.Unmarshal(b, &signedTx) - if err != nil { - return nil, err - } - - _, err = govalidator.ValidateStruct(signedTx) - if err != nil { - return nil, err - } - - digestHeader, exists := signedTx.Headers["digest"] - if !exists { - return nil, errors.New("a transaction signature must cover the request body via digest") - } - - var digestInst digest.Instance - err = digestInst.UnmarshalText([]byte(digestHeader)) - if err != nil { - return nil, err - } - - if !digestInst.Verify([]byte(signedTx.Body)) { - return nil, errors.New("the digest header does not match the included body") - } - - var req http.Request - sigParams, err := signedTx.Extract(&req) - if err != nil { - return nil, err - } - - exists = false - for _, header := range sigParams.Headers { - if header == "digest" { - exists = true - } - } - if !exists { - return nil, errors.New("a transaction signature must cover the request body via digest") - } - - valid, err := sigParams.Verify(w.PubKey, crypto.Hash(0), &req) - if err != nil { - return nil, err - } - if !valid { - return nil, errors.New("the signature is invalid") - } - - var transactionRecode transactionRequestRecode - err = json.Unmarshal([]byte(signedTx.Body), &transactionRecode) - if err != nil { - return nil, err - } - - if !govalidator.IsEmail(transactionRecode.Destination) { - if !validators.IsUUID(transactionRecode.Destination) { - if !validators.IsBTCAddress(transactionRecode.Destination) { - if !validators.IsETHAddressNoChecksum(transactionRecode.Destination) { - return nil, fmt.Errorf("%s is not a valid destination", transactionRecode.Destination) - } - } - } - } - - // NOTE we are effectively stuck using two different JSON parsers on the same data as our parser - // is different than Uphold's. this has the unfortunate effect of opening us to attacks - // that exploit differences between parsers. to mitigate this we will be extremely strict - // in parsing, requiring that the remarshalled struct is equivalent. this means the order - // of fields must be identical as well as numeric serialization. for encoding/json, note - // that struct keys are serialized in the order they are defined - - remarshalledBody, err := json.Marshal(&transactionRecode) - if err != nil { - return nil, err - } - if string(remarshalledBody) != signedTx.Body { - return nil, errors.New("the remarshalled body must be identical") - } - - var transaction transactionRequest - err = json.Unmarshal([]byte(signedTx.Body), &transaction) - if err != nil { - return nil, err - } - return &transaction, nil -} - -// VerifyTransaction verifies that the transaction is valid -// NOTE VerifyTransaction guards against transactions that seek to exploit parser differences -// such as including additional fields that are not understood by this implementation but may -// be understood by the upstream wallet provider. See DecodeTransaction for details. -func (w *Wallet) VerifyTransaction(ctx context.Context, transactionB64 string) (*walletutils.TransactionInfo, error) { - transaction, err := w.decodeTransaction(transactionB64) - if err != nil { - return nil, err - } - var info walletutils.TransactionInfo - info.Probi = transaction.Denomination.Currency.ToProbi(transaction.Denomination.Amount) - { - tmp := *transaction.Denomination.Currency - info.AltCurrency = &tmp - } - info.Destination = transaction.Destination - - return &info, err -} - -// VerifyAnonCardTransaction calls VerifyTransaction and checks the currency, amount and destination -func (w *Wallet) VerifyAnonCardTransaction(ctx context.Context, transactionB64 string, requiredDestination string) (*walletutils.TransactionInfo, error) { - txInfo, err := w.VerifyTransaction(ctx, transactionB64) - if err != nil { - return nil, err - } - if *txInfo.AltCurrency != altcurrency.BAT { - return nil, errors.New("only BAT denominated transactions are supported for anon cards") - } - if txInfo.Probi.LessThan(decimal.Zero) { - return nil, errors.New("anon card transaction cannot be for negative BAT") - } - if requiredDestination != "" && txInfo.Destination != requiredDestination { - return nil, errors.New("anon card transactions must have settlement as their destination") - } - - return txInfo, nil -} - -type upholdTransactionResponseDestinationNodeUser struct { - ID string `json:"id"` - CitizenshipCountry string `json:"citizenshipCountry"` - IdentityCountry string `json:"identityCountry"` - ResidenceCountry string `json:"residenceCountry"` -} - -type upholdTransactionResponseDestinationNode struct { - Type string `json:"type"` - ID string `json:"id"` - User upholdTransactionResponseDestinationNodeUser `json:"user"` -} - -type upholdTransactionResponseDestination struct { - Type string `json:"type"` - CardID string `json:"CardId,omitempty"` - Node upholdTransactionResponseDestinationNode `json:"node,omitempty"` - Currency string `json:"currency"` - Amount decimal.Decimal `json:"amount"` - ExchangeFee decimal.Decimal `json:"commission"` - TransferFee decimal.Decimal `json:"fee"` - IsMember bool `json:"isMember"` -} - -type upholdTransactionResponseParams struct { - TTL int64 `json:"ttl"` -} - -type upholdTransactionResponse struct { - Status string `json:"status"` - ID string `json:"id"` - Denomination denomination `json:"denomination"` - Destination upholdTransactionResponseDestination `json:"destination"` - Origin upholdTransactionResponseDestination `json:"origin"` - Params upholdTransactionResponseParams `json:"params"` - CreatedAt string `json:"createdAt"` - Message string `json:"message"` -} - -func (resp upholdTransactionResponse) ToTransactionInfo() *walletutils.TransactionInfo { - var txInfo walletutils.TransactionInfo - txInfo.Probi = resp.Denomination.Currency.ToProbi(resp.Denomination.Amount) - { - tmp := *resp.Denomination.Currency - txInfo.AltCurrency = &tmp - } - destination := resp.Destination - destinationNode := destination.Node - txInfo.UserID = destinationNode.User.ID - if len(destination.CardID) > 0 { - txInfo.Destination = destination.CardID - } else if len(destinationNode.ID) > 0 { - txInfo.Destination = destinationNode.ID - } - - if len(resp.Origin.CardID) > 0 { - txInfo.Source = resp.Origin.CardID - } else if len(resp.Origin.Node.ID) > 0 { - txInfo.Source = resp.Origin.Node.ID - } - - var err error - txInfo.Time, err = time.Parse(dateFormat, resp.CreatedAt) - if err != nil { - log.Fatalf("%s is not a valid ISO 8601 datetime\n", resp.CreatedAt) - } - - txInfo.DestCurrency = destination.Currency - txInfo.DestAmount = destination.Amount - txInfo.TransferFee = destination.TransferFee - txInfo.ExchangeFee = destination.ExchangeFee - txInfo.Status = resp.Status - if txInfo.Status == "pending" { - txInfo.ValidUntil = time.Now().UTC().Add(time.Duration(resp.Params.TTL) * time.Millisecond) - } - txInfo.ID = resp.ID - txInfo.Note = resp.Message - txInfo.KYC = destination.IsMember - - txInfo.CitizenshipCountry = destination.Node.User.CitizenshipCountry - txInfo.IdentityCountry = destination.Node.User.IdentityCountry - txInfo.ResidenceCountry = destination.Node.User.ResidenceCountry - - return &txInfo -} - -// SubmitTransaction submits the base64 encoded transaction for verification but does not move funds -// -// unless confirm is set to true. -func (w *Wallet) SubmitTransaction(ctx context.Context, transactionB64 string, confirm bool) (*walletutils.TransactionInfo, error) { - logger := logging.FromContext(ctx) - - _, err := w.VerifyTransaction(ctx, transactionB64) - if err != nil { - return nil, err - } - - b, err := base64.StdEncoding.DecodeString(transactionB64) - if err != nil { - return nil, err - } - var signedTx httpsignature.HTTPSignedRequest - err = json.Unmarshal(b, &signedTx) - if err != nil { - return nil, err - } - - url := "/v0/me/cards/" + w.ProviderID + "/transactions" - if confirm { - url = url + "?commit=true" - } - - req, err := newRequest("POST", url, nil) - if err != nil { - return nil, err - } - - _, err = signedTx.Extract(req) - if err != nil { - return nil, err - } - - respBody, _, err := submit(logger, req) - if err != nil { - return nil, err - } - - var uhResp upholdTransactionResponse - err = json.Unmarshal(respBody, &uhResp) - if err != nil { - return nil, err - } - - return uhResp.ToTransactionInfo(), nil -} - -// ConfirmTransaction confirms a previously submitted transaction, moving funds -func (w *Wallet) ConfirmTransaction(ctx context.Context, id string) (*walletutils.TransactionInfo, error) { - logger := logging.FromContext(ctx) - - req, err := newRequest("POST", "/v0/me/cards/"+w.ProviderID+"/transactions/"+id+"/commit", nil) - if err != nil { - return nil, err - } - body, _, err := submit(logger, req) - if err != nil { - return nil, err - } - - var uhResp upholdTransactionResponse - err = json.Unmarshal(body, &uhResp) - if err != nil { - return nil, err - } - - if uhResp.Destination.Type != "card" && uhResp.Destination.Type != "anonymous" { - panic("Confirming a non-card transaction is not supported!!!") - } - - return uhResp.ToTransactionInfo(), nil -} - -// GetTransaction returns info about a previously confirmed transaction -func (w *Wallet) GetTransaction(ctx context.Context, id string) (*walletutils.TransactionInfo, error) { - logger := logging.FromContext(ctx) - - req, err := newRequest("GET", "/v0/me/transactions/"+id, nil) - if err != nil { - return nil, err - } - body, _, err := submit(logger, req) - if err != nil { - return nil, err - } - - var uhResp upholdTransactionResponse - err = json.Unmarshal(body, &uhResp) - if err != nil { - return nil, err - } - - return uhResp.ToTransactionInfo(), nil -} - -// ListTransactions for this wallet, pagination not yet supported -func (w *Wallet) ListTransactions(ctx context.Context, limit int, startDate time.Time) ([]walletutils.TransactionInfo, error) { - logger := logging.FromContext(ctx) - - var out []walletutils.TransactionInfo - if limit > 0 { - out = make([]walletutils.TransactionInfo, 0, limit) - } - var totalTransactions int - toExit := false - for { - req, err := newRequest("GET", "/v0/me/cards/"+w.ProviderID+"/transactions", nil) - if err != nil { - return nil, err - } - - start := len(out) - stop := start + batchSize - if limit > 0 && stop >= limit { - stop = limit - 1 - } - if totalTransactions != 0 && stop >= totalTransactions { - stop = totalTransactions - 1 - } - - req.Header.Set("Range", fmt.Sprintf("items=%d-%d", start, stop)) - var body []byte - var resp *http.Response - for i := 0; i < listTransactionsRetries; i++ { - body, resp, err = submit(logger, req) - if nerr, ok := err.(net.Error); ok && nerr.Temporary() { - logger.Debug(). - Str("path", "github.com/brave-intl/bat-go/wallet/provider/uphold"). - Str("type", "net.Error"). - Msg("Temporary error occurred, retrying") - continue - } - break - } - if err != nil { - return nil, err - } - - contentRange := resp.Header.Get("Content-Range") - parts := strings.Split(contentRange, "/") - if len(parts) != 2 { - return nil, errors.New("invalid Content-Range header returned") - } - - tmp, err := strconv.Atoi(parts[1]) - if err != nil { - return nil, err - } - totalTransactions = int(tmp) - - var uhResp []upholdTransactionResponse - err = json.Unmarshal(body, &uhResp) - if err != nil { - return nil, err - } - - for i := 0; i < len(uhResp); i++ { - txInfo := *uhResp[i].ToTransactionInfo() - if txInfo.Time.Before(startDate) { - toExit = true - break - } - out = append(out, txInfo) - if len(out) == limit { - break - } - } - - if len(out) == limit || len(out) == totalTransactions || toExit { - break - } - } - return out, nil -} - -// GetBalance returns the last known balance, if refresh is true then the current balance is fetched -func (w *Wallet) GetBalance(ctx context.Context, refresh bool) (*walletutils.Balance, error) { - if !refresh { - return w.LastBalance, nil - } - - var balance walletutils.Balance - - details, err := w.GetCardDetails(ctx) - if err != nil { - return nil, err - } - - if details.Currency != *w.AltCurrency { - return nil, errors.New("returned currency did not match wallet altcurrency") - } - - balance.TotalProbi = details.Currency.ToProbi(details.Balance) - balance.SpendableProbi = details.Currency.ToProbi(details.AvailableBalance) - balance.ConfirmedProbi = balance.SpendableProbi - balance.UnconfirmedProbi = balance.TotalProbi.Sub(balance.SpendableProbi) - w.LastBalance = &balance - - return &balance, nil -} - -type createCardAddressRequest struct { - Network string `json:"network"` -} - -type createCardAddressResponse struct { - ID string `json:"id"` -} - -// CreateCardAddress on network, returning the address -func (w *Wallet) CreateCardAddress(ctx context.Context, network string) (string, error) { - logger := logging.FromContext(ctx) - - reqPayload := createCardAddressRequest{Network: network} - payload, err := json.Marshal(reqPayload) - if err != nil { - return "", err - } - - req, err := newRequest("POST", fmt.Sprintf("/v0/me/cards/%s/addresses", w.ProviderID), bytes.NewBuffer(payload)) - if err != nil { - return "", err - } - - body, _, err := submit(logger, req) - if err != nil { - return "", err - } - - var details createCardAddressResponse - err = json.Unmarshal(body, &details) - if err != nil { - return "", err - } - return details.ID, nil -} - -// FundWallet should fund a given wallet from the donor card (only used in wallet testing) -func FundWallet(ctx context.Context, destWallet *Wallet, amount decimal.Decimal) (decimal.Decimal, error) { - var donorInfo walletutils.Info - donorInfo.Provider = "uphold" - donorInfo.ProviderID = os.Getenv("DONOR_WALLET_CARD_ID") - { - tmp := altcurrency.BAT - donorInfo.AltCurrency = &tmp - } - zero := decimal.NewFromFloat(0) - donorWalletPublicKeyHex := os.Getenv("DONOR_WALLET_PUBLIC_KEY") - donorWalletPrivateKeyHex := os.Getenv("DONOR_WALLET_PRIVATE_KEY") - var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey - donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) - if err != nil { - return zero, err - } - donorPrivateKey, err = hex.DecodeString(donorWalletPrivateKeyHex) - if err != nil { - return zero, err - } - donorWallet := &Wallet{Info: donorInfo, PrivKey: donorPrivateKey, PubKey: donorPublicKey} - - if len(donorWallet.ID) > 0 { - return zero, errors.New("donor wallet does not have an ID") - } - - _, err = donorWallet.Transfer(ctx, altcurrency.BAT, altcurrency.BAT.ToProbi(amount), destWallet.Info.ProviderID) - if err != nil { - return zero, err - } - - balance, err := destWallet.GetBalance(ctx, true) - if err != nil { - return zero, err - } - - return balance.TotalProbi, nil -} diff --git a/libs/wallet/provider/uphold/uphold_test.go b/libs/wallet/provider/uphold/uphold_test.go deleted file mode 100644 index 3dca03225..000000000 --- a/libs/wallet/provider/uphold/uphold_test.go +++ /dev/null @@ -1,350 +0,0 @@ -package uphold - -import ( - "context" - "encoding/hex" - "errors" - "net/http" - "net/url" - "os" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/pindialer" - "github.com/brave-intl/bat-go/libs/wallet" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "golang.org/x/crypto/ed25519" - "gotest.tools/assert" -) - -func TestGetCardDetails(t *testing.T) { - ctx := context.Background() - - if os.Getenv("UPHOLD_ACCESS_TOKEN") == "" { - t.Skip("skipping test; UPHOLD_ACCESS_TOKEN not set") - } - - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = "6654ecb0-6079-4f6c-ba58-791cc890a561" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - wallet, err := FromWalletInfo(ctx, info) - if err != nil { - t.Error(err) - } - _, err = wallet.GetBalance(ctx, true) - if err != nil { - t.Error(err) - } -} - -func TestRegister(t *testing.T) { - ctx := context.Background() - - if os.Getenv("UPHOLD_ACCESS_TOKEN") == "" { - t.Skip("skipping test; UPHOLD_ACCESS_TOKEN not set") - } - - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = "" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - if err != nil { - t.Fatal(err) - } - - destWallet := &Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} - err = destWallet.Register(ctx, "bat-go test card") - if err != nil { - t.Error(err) - } -} - -func TestDecodeTransaction(t *testing.T) { - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = uuid.NewV4().String() - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - wallet, err := FromWalletInfo(context.Background(), info) - if err != nil { - t.Error(err) - } - - var pk httpsignature.Ed25519PubKey - pk, err = hex.DecodeString("424073b208e97af51cab7a389bcfe6942a3b7c7520fe9dab84f311f7846f5fcf") - if err != nil { - t.Error(err) - } - wallet.PubKey = pk - - txnB64 := "eyJoZWFkZXJzIjp7ImRpZ2VzdCI6IlNIQS0yNTY9WFg0YzgvM0J4ejJkZWNkakhpY0xWaXJ5dTgxbWdGNkNZTTNONFRHc0xoTT0iLCJzaWduYXR1cmUiOiJrZXlJZD1cInByaW1hcnlcIixhbGdvcml0aG09XCJlZDI1NTE5XCIsaGVhZGVycz1cImRpZ2VzdFwiLHNpZ25hdHVyZT1cIjI4TitabzNodlRRWmR2K2trbGFwUE5IY29OMEpLdWRiSU5GVnlOSm0rWDBzdDhzbXdzYVlHaTJQVHFRbjJIVWdacUp4Q2NycEpTMWpxZHdyK21RNEN3PT1cIiJ9LCJvY3RldHMiOiJ7XCJkZW5vbWluYXRpb25cIjp7XCJhbW91bnRcIjpcIjI1XCIsXCJjdXJyZW5jeVwiOlwiQkFUXCJ9LFwiZGVzdGluYXRpb25cIjpcImZvb0BiYXIuY29tXCJ9In0=" - - txnReq, err := wallet.decodeTransaction(txnB64) - if err != nil { - t.Error(err) - } - - var expected transactionRequest - expected.Destination = "foo@bar.com" - expected.Denomination.Amount, err = decimal.NewFromString("25.0") - if err != nil { - t.Error(err) - } - { - tmp := altcurrency.BAT - expected.Denomination.Currency = &tmp - } - - if txnReq.Destination != expected.Destination { - t.Error("Decoded transaction does not match expected value") - } - if !txnReq.Denomination.Amount.Equal(expected.Denomination.Amount) { - t.Error("Decoded transaction does not match expected value") - } - if *txnReq.Denomination.Currency != *expected.Denomination.Currency { - t.Error("Decoded transaction does not match expected value") - } -} - -func TestReMarshall(t *testing.T) { - // FIXME - //{"denomination":{"amount":"50.000000000000000000","currency":"BAT"},"destination":"99f7ee1c-bce7-4b11-bb91-825412f4764b"}} -} - -func TestVerifyTransaction(t *testing.T) { - // FIXME test malicious signature cases -} - -func TestTransactions(t *testing.T) { - ctx := context.Background() - - if os.Getenv("UPHOLD_ACCESS_TOKEN") == "" { - t.Skip("skipping test; UPHOLD_ACCESS_TOKEN not set") - } - if os.Getenv("DONOR_WALLET_PUBLIC_KEY") == "" { - t.Skip("skipping test; DONOR_WALLET_PUBLIC_KEY not set") - } - if os.Getenv("DONOR_WALLET_PRIVATE_KEY") == "" { - t.Skip("skipping test; DONOR_WALLET_PRIVATE_KEY not set") - } - if os.Getenv("DONOR_WALLET_CARD_ID") == "" { - t.Skip("skipping test; DONOR_WALLET_CARD_ID not set") - } - - var donorInfo wallet.Info - donorInfo.Provider = "uphold" - donorInfo.ProviderID = os.Getenv("DONOR_WALLET_CARD_ID") - { - tmp := altcurrency.BAT - donorInfo.AltCurrency = &tmp - } - - donorWalletPublicKeyHex := os.Getenv("DONOR_WALLET_PUBLIC_KEY") - donorWalletPrivateKeyHex := os.Getenv("DONOR_WALLET_PRIVATE_KEY") - var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey - donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) - if err != nil { - t.Fatal(err) - } - donorPrivateKey, err = hex.DecodeString(donorWalletPrivateKeyHex) - if err != nil { - t.Fatal(err) - } - donorWallet := &Wallet{Info: donorInfo, PrivKey: donorPrivateKey, PubKey: donorPublicKey} - - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = "" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - if err != nil { - t.Fatal(err) - } - - destWallet := &Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} - err = destWallet.Register(ctx, "bat-go test transaction card") - if err != nil { - t.Error(err) - } - - value, err := decimal.NewFromString("10") - if err != nil { - t.Error(err) - } - - tx, err := donorWallet.PrepareTransaction( - altcurrency.BAT, - altcurrency.BAT.ToProbi(value), - destWallet.Info.ProviderID, - "bat-go:uphold.TestTransactions", - "", - nil, - ) - if err != nil { - t.Error(err) - } - - submitInfo, err := donorWallet.SubmitTransaction(ctx, tx, false) - if err != nil { - t.Error(err) - } - - balance, err := destWallet.GetBalance(ctx, true) - if err != nil { - t.Error(err) - } - - if balance.TotalProbi.GreaterThan(decimal.Zero) { - t.Error("Submit without confirm should not result in a balance.") - } - - // Submitted but unconfirmed transactions cannot be retrieved via GetTransaction - _, err = donorWallet.GetTransaction(ctx, submitInfo.ID) - if err == nil { - t.Error("Expected error retrieving unconfirmed transaction") - } - if !errorutils.IsErrNotFound(err) { - t.Error("Expected \"missing\" transaction as error cause") - } - - commitInfo, err := donorWallet.ConfirmTransaction(ctx, submitInfo.ID) - if err != nil { - t.Error(err) - } - - if commitInfo.ID != submitInfo.ID { - t.Error("Transaction id mismatch!") - } - - if commitInfo.Destination != destWallet.ProviderID { - t.Error("Transaction destination mismatch!") - } - - if !commitInfo.Probi.Equals(submitInfo.Probi) { - t.Error("Transaction probi mismatch!") - } - - getInfo, err := donorWallet.GetTransaction(ctx, submitInfo.ID) - if err != nil { - t.Error(err) - } - - if getInfo.ID != submitInfo.ID { - t.Error("Transaction id mismatch!") - } - - if getInfo.Destination != destWallet.ProviderID { - t.Error("Transaction destination mismatch!") - } - - if !getInfo.Probi.Equals(submitInfo.Probi) { - t.Error("Transaction probi mismatch!") - } - - balance, err = destWallet.GetBalance(ctx, true) - if err != nil { - t.Error(err) - } - - if balance.TotalProbi.Equals(decimal.Zero) { - t.Error("Submit with confirm should result in a balance.") - } - - // wait for funds to be available - <-time.After(1 * time.Second) - txInfo, err := destWallet.Transfer(ctx, altcurrency.BAT, submitInfo.Probi, donorWallet.ProviderID) - if err != nil { - t.Error(err) - } - if txInfo == nil { - t.Error("no tx information from transfer!") - } - - balance, err = destWallet.GetBalance(ctx, true) - if err != nil { - t.Error(err) - } - - if !balance.TotalProbi.Equals(decimal.Zero) { - t.Error("Transfer should move balance back to donorWallet.") - } - - if !submitInfo.Probi.Equals(txInfo.Probi) { - t.Error("Transaction amount should match") - } - - if len(txInfo.ID) == 0 { - t.Error("Transaction should have identifier") - } -} - -func TestFingerprintCheck(t *testing.T) { - var proxy func(*http.Request) (*url.URL, error) - wrongFingerprint := "IYSLsapSKlkofKfi6M2hmS4gzXbQKGIX/DHBWIgstw3=" - - client = &http.Client{ - Timeout: time.Second * 60, - // remove middleware calls - Transport: &http.Transport{ - Proxy: proxy, - DialTLSContext: pindialer.MakeContextDialer(wrongFingerprint), - }, - } - - w := requireDonorWallet(t) - - req, err := w.signRegistration("randomlabel") - if err != nil { - t.Error(err) - } - - _, err = client.Do(req) - // should fail here - if err == nil { - t.Error("unable to fail with bad cert") - } - assert.Equal(t, errors.Unwrap(err).Error(), "failed to validate certificate chain: the server certificate was not valid") -} - -func requireDonorWallet(t *testing.T) *Wallet { - if os.Getenv("UPHOLD_ACCESS_TOKEN") == "" { - t.Skip("skipping test; UPHOLD_ACCESS_TOKEN not set") - } - - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = "" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - if err != nil { - t.Fatal(err) - } - - return &Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} -} diff --git a/libs/wallet/wallet.go b/libs/wallet/wallet.go deleted file mode 100644 index 1c3d89aac..000000000 --- a/libs/wallet/wallet.go +++ /dev/null @@ -1,91 +0,0 @@ -// Package wallet defines common datastructures and an interface for cryptocurrency wallets -package wallet - -import ( - "context" - "fmt" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// Info contains information about a wallet like associated identifiers, the denomination, -// the last known balance and provider -type Info struct { - ID string `json:"paymentId" valid:"uuidv4,optional" db:"id"` - Provider string `json:"provider" valid:"in(uphold,brave)" db:"provider"` - ProviderID string `json:"providerId" valid:"uuidv4" db:"provider_id"` - AltCurrency *altcurrency.AltCurrency `json:"altcurrency" valid:"-"` - PublicKey string `json:"publicKey,omitempty" valid:"hexadecimal,optional" db:"public_key"` - LastBalance *Balance `json:"balances,omitempty" valid:"-"` - ProviderLinkingID *uuid.UUID `json:"providerLinkingId" valid:"-" db:"provider_linking_id"` - AnonymousAddress *uuid.UUID `json:"anonymousAddress" valid:"-" db:"anonymous_address"` - UserDepositAccountProvider *string `json:"userDepositAccountProvider" valid:"in(uphold)" db:"user_deposit_account_provider"` - UserDepositDestination string `json:"userDepositCardId" db:"user_deposit_destination"` -} - -// TransactionInfo contains information about a transaction like the denomination, amount in probi, -// destination address, status and identifier -type TransactionInfo struct { - Probi decimal.Decimal `json:"probi"` - AltCurrency *altcurrency.AltCurrency `json:"altcurrency"` - Destination string `json:"address"` - TransferFee decimal.Decimal `json:"fee"` - ExchangeFee decimal.Decimal `json:"-"` - Status string `json:"status"` - ID string `json:"id"` - DestCurrency string `json:"-"` - DestAmount decimal.Decimal `json:"-"` - ValidUntil time.Time `json:"-"` - Source string `json:"-"` - Time time.Time `json:"-"` - Note string `json:"-"` - UserID string `json:"-"` - KYC bool `json:"-"` - CitizenshipCountry string `json:"-"` - IdentityCountry string `json:"-"` - ResidenceCountry string `json:"-"` -} - -// String returns the transaction info as an easily readable string -func (t TransactionInfo) String() string { - return fmt.Sprintf("%s: %s %s sent from %s to %s, charged transfer fee %s and exchange fee %s, destination recieved %s %s", t.Time, - t.AltCurrency.FromProbi(t.Probi), t.AltCurrency, t.Source, t.Destination, t.TransferFee, t.ExchangeFee, t.DestAmount, t.DestCurrency) -} - -// ByTime implements sort.Interface for []TransactionInfo based on the Time field. -type ByTime []TransactionInfo - -func (a ByTime) Len() int { return len(a) } -func (a ByTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByTime) Less(i, j int) bool { return a[i].Time.Before(a[j].Time) } - -// Balance holds balance information for a wallet -type Balance struct { - TotalProbi decimal.Decimal - SpendableProbi decimal.Decimal - ConfirmedProbi decimal.Decimal - UnconfirmedProbi decimal.Decimal -} - -// Wallet is an interface for a cryptocurrency wallet -type Wallet interface { - GetWalletInfo() Info - // Transfer moves funds out of the associated wallet and to the specific destination - Transfer(ctx context.Context, altcurrency altcurrency.AltCurrency, probi decimal.Decimal, destination string) (*TransactionInfo, error) - // VerifyTransaction verifies that the base64 encoded transaction is valid - // NOTE VerifyTransaction must guard against transactions that seek to exploit parser differences - // such as including additional fields that are not understood by local implementation but may - // be understood by the upstream wallet provider. - VerifyTransaction(ctx context.Context, transactionB64 string) (*TransactionInfo, error) - // SubmitTransaction submits the base64 encoded transaction for verification but does not move funds - SubmitTransaction(ctx context.Context, transactionB64 string, confirm bool) (*TransactionInfo, error) - // ConfirmTransaction confirms a previously submitted transaction, moving funds - ConfirmTransaction(ctx context.Context, id string) (*TransactionInfo, error) - // GetBalance returns the last known balance, if refresh is true then the current balance is fetched - GetBalance(ctx context.Context, refresh bool) (*Balance, error) - // ListTransactions for this wallet, limit number of transactions returned - ListTransactions(ctx context.Context, limit int, startDate time.Time) ([]TransactionInfo, error) -} diff --git a/main/go.mod b/main/go.mod deleted file mode 100644 index b02de3d7e..000000000 --- a/main/go.mod +++ /dev/null @@ -1,249 +0,0 @@ -module github.com/brave-intl/bat-go/main - -go 1.18 - -replace github.com/brave-intl/bat-go/cmd => ../cmd - -replace github.com/brave-intl/bat-go/services => ../services - -replace github.com/brave-intl/bat-go/libs => ../libs - -replace github.com/brave-intl/bat-go/tools => ../tools - -require ( - github.com/brave-intl/bat-go/cmd v1.0.2 - github.com/brave-intl/bat-go/libs v1.0.2 - github.com/brave-intl/bat-go/services v1.0.2 - github.com/brave-intl/bat-go/tools v1.0.2 -) - -require ( - cloud.google.com/go/compute v1.18.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.11.0 // indirect - cloud.google.com/go/kms v1.6.0 // indirect - cloud.google.com/go/monitoring v1.8.0 // indirect - filippo.io/age v1.1.1 // indirect - filippo.io/edwards25519 v1.0.0 // indirect - github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.28 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/DataDog/datadog-go v4.8.3+incompatible // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b // indirect - github.com/aliyun/alibaba-cloud-sdk-go v1.61.1831 // indirect - github.com/amazon-ion/ion-go v1.2.0 // indirect - github.com/amzn/ion-go v1.1.3 // indirect - github.com/amzn/ion-hash-go v1.1.2 // indirect - github.com/armon/go-metrics v0.4.1 // indirect - github.com/armon/go-radix v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/awa/go-iap v1.3.22 // indirect - github.com/aws/aws-sdk-go v1.44.206 // indirect - github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/config v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.12.23 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 // indirect - github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6 // indirect - github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 // indirect - github.com/aws/smithy-go v1.13.5 // indirect - github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/blocto/solana-go-sdk v1.27.0 // indirect - github.com/btcsuite/btcutil v1.0.2 // indirect - github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect - github.com/circonus-labs/circonusllhist v0.1.5 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/go-chi/cors v1.2.1 // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect - github.com/go-openapi/analysis v0.21.4 // indirect - github.com/go-openapi/errors v0.20.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/loads v0.21.2 // indirect - github.com/go-openapi/spec v0.20.7 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-openapi/validate v0.22.0 // indirect - github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.11.1 // indirect - github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 // indirect - github.com/golang-migrate/migrate/v4 v4.15.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/go-metrics-stackdriver v0.5.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.3.1 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-kms-wrapping/v2 v2.0.5 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.4 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.0 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.5 // indirect - github.com/hashicorp/go-retryablehttp v0.7.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hashicorp/hcp-sdk-go v0.23.0 // indirect - github.com/hashicorp/vault v1.12.7 // indirect - github.com/hashicorp/vault/api v1.8.1 // indirect - github.com/hashicorp/vault/sdk v0.6.1-0.20230427140652-b4b396ffc14f // indirect - github.com/hashicorp/yamux v0.1.1 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/huandu/xstrings v1.3.2 // indirect - github.com/iancoleman/orderedmap v0.2.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 // indirect - github.com/klauspost/compress v1.15.15 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/lib/pq v1.10.7 // indirect - github.com/linkedin/goavro v2.1.0+incompatible // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/mitchellh/cli v1.1.4 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect - github.com/mssola/user_agent v0.5.3 // indirect - github.com/natefinch/atomic v1.0.1 // indirect - github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 // indirect - github.com/oklog/run v1.1.0 // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/oracle/oci-go-sdk/v60 v60.0.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/pierrec/lz4 v2.6.1+incompatible // indirect - github.com/pierrec/lz4/v4 v4.1.17 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/posener/complete v1.2.3 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.28.0 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/segmentio/kafka-go v0.4.35 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/sony/gobreaker v0.5.0 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.13.0 // indirect - github.com/square/go-jose v2.6.0+incompatible // indirect - github.com/stretchr/testify v1.8.4 // indirect - github.com/stripe/stripe-go/v72 v72.122.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect - github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/x448/float16 v0.8.4 // indirect - go.mongodb.org/mongo-driver v1.10.3 // indirect - go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.1.0 // indirect - golang.org/x/tools v0.14.0 // indirect - google.golang.org/api v0.110.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/macaroon.v2 v2.1.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/main/go.sum b/main/go.sum deleted file mode 100644 index 6bc5f429b..000000000 --- a/main/go.sum +++ /dev/null @@ -1,2470 +0,0 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.101.1/go.mod h1:55HwjsGW4CHD3JrNuMdZtSDsgTs0CuCB/bBTugD+7AA= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.11.0 h1:kwCWfKwB6ePZoZnGLwrd3B6Ru/agoHANTUBWpVNIdnM= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/monitoring v1.5.0/go.mod h1:/o9y8NYX5j91JjD/JvGLYbi86kL11OjyJXq2XziLJu4= -cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/spanner v1.28.0/go.mod h1:7m6mtQZn/hMbMfx62ct5EWrGND4DNqkXyrmBPRS+OJo= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo= -github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= -github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= -github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= -github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b h1:doCpXjVwui6HUN+xgNsNS3SZ0/jUZ68Eb+mJRNOZfog= -github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1831 h1:g7YHKEArwtJd4mynWxfzWCTMkqRzqa0QpuF2enx8WkQ= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1831/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= -github.com/amazon-ion/ion-go v1.2.0 h1:EgFy23/7gRxRYdUkJARh/7eZc8BYkFFDZZSqB3PwVqQ= -github.com/amazon-ion/ion-go v1.2.0/go.mod h1:3ZEje8i20TiIPVZlN+KE3B2ppZ1B8d9F/KaT7Dtec+k= -github.com/amzn/ion-go v1.1.3 h1:gGhjtLY0GUNQXej5N2qHhoVWQBkgtoPDt1feYYFMfOc= -github.com/amzn/ion-go v1.1.3/go.mod h1:7wQBWQ7PhPpZCr9PL+mtuIyNmyLjuV8qt2mrfxmvkA8= -github.com/amzn/ion-hash-go v1.1.2 h1:cUEolXoS7aPwYFknwae47zppF+gJgZEWqRiRbPdPIy8= -github.com/amzn/ion-hash-go v1.1.2/go.mod h1:6DKfguDnpHlHE8fHV7CxZiWnEudDxMDXUkSn2fu3j/4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= -github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.11/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/awa/go-iap v1.3.22 h1:cfMplB/bwo9guOFblbaTme42VJ4hMUELduniM+pq4iM= -github.com/awa/go-iap v1.3.22/go.mod h1:DbAmBQTIePitXo8iqsPgsQNe9rLkp85SYk1XOEv3vxE= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.44.206 h1:xC7O40wdnKH4A95KdYt+smXl9hig1vu9b3mFxAxUoak= -github.com/aws/aws-sdk-go v1.44.206/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0= -github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= -github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 h1:RKci2D7tMwpvGpDNZnGQw9wk6v7o/xSwFcUAuNPoB8k= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9/go.mod h1:vCmV1q1VK8eoQJ5+aYE7PkK1K6v41qJ5pJdK3ggCDvg= -github.com/aws/aws-sdk-go-v2/config v1.6.0/go.mod h1:TNtBVmka80lRPk5+S9ZqVfFszOQAGJJ9KbT3EM3CHNU= -github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/config v1.17.10 h1:zBy5QQ/mkvHElM1rygHPAzuH+sl8nsdSaxSWj0+rpdE= -github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= -github.com/aws/aws-sdk-go-v2/credentials v1.3.2/go.mod h1:PACKuTJdt6AlXvEq8rFI4eDmoqDFC5DpVKQbWysaDgM= -github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23 h1:LctvcJMIb8pxvk5hQhChpCu0WlU6oKQmcYb1HA4IZSA= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23/go.mod h1:0awX9iRr/+UO7OwRQFpV1hNtXxOVuehpjVEzrIAYNcA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.4.0/go.mod h1:Mj/U8OpDbcVcoctrYwA2bak8k/HFPdcLzI/vaiXMwuM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 h1:E3PXZSI3F2bzyj6XxUXdTIfvp425HHhwKsFvmzBwHgs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.4.0/go.mod h1:eHwXu2+uE/T6gpnYWwBwqoeqRf9IXyCcolyOWDRAErQ= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.4/go.mod h1:Ex7XQmbFmgFHrjUX6TN3mApKW5Hglyga+F7wZHTtYhA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.0/go.mod h1:Q5jATQc+f1MfZp3PDMhn6ry18hGvE0i8yvbXoKbnZaE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 h1:Mza+vlnZr+fPKFKRq/lKGVvM6B/8ZZmNdEopOwSQLms= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 h1:2EXB7dtGwRYIN3XQ9qwIW504DVbKIw3r89xQnonGdsQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16/go.mod h1:XH+3h395e3WVdd6T2Z3mPxuI+x/HVtdqVOREkTiyubs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.2.2/go.mod h1:EASdTcM1lGhUe1/p4gkojHwlGJkeoRjjr1sRCzup3Is= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 h1:dpiPHgmFstgkLG07KaYAewvuptq5kvo52xn7tVSrtrQ= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10/go.mod h1:9cBNUHI2aW4ho0A5T87O294iPDuuUOSIEDjnd1Lq/z0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 h1:KSvtm1+fPXE0swe9GPjc6msyrdTT0LB/BP8eLugL1FI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20/go.mod h1:Mp4XI/CkWGD79AQxZ5lIFlgvC0A+gl+4BmyG1F+SfNc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.2/go.mod h1:NXmNI41bdEsJMrD0v9rUvbGCB5GwdBEpKvUvIY3vTFg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 h1:GE25AWCdNUPh9AOJzI9KIJnja7IwUc1WyUqz/JTyJ/I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.5.2/go.mod h1:QuL2Ym8BkrLmN4lUofXYq6000/i5jPjosCNK//t6gak= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.2/go.mod h1:np7TMuJNT83O0oDOSF8i4dF3dvGqA6hPYYo6YYkzgRA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 h1:piDBAaWkaxkkVV3xJJbTehXCZRXYs49kvpi/LG6LR2o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19/go.mod h1:BmQWRVkLTmyNzYPFAZgon53qKLWBNSvonugD1MrSWUs= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4= -github.com/aws/aws-sdk-go-v2/service/qldb v1.14.20/go.mod h1:9morR/lAo8ziBkYz5gxtGe0FzAjkcUfxAqSk0Q9obDc= -github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6 h1:BFK9rvyhv4OyxrZa0w4ItE2s5L2Zg/hMu9jA286YWvg= -github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6/go.mod h1:+hUHf6G2dhZJcVUVNqqCmjezYNZu07akDOtc72upBEQ= -github.com/aws/aws-sdk-go-v2/service/qldbsession v1.13.19/go.mod h1:xX3iRpzN9iJYgP45OUYHJWFLrre0/s1Mur8SjOwg3RU= -github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10 h1:xZuYRwfZ1nFBWQVjXaegmvZcqsfsbzDYyXxXXdujKN4= -github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10/go.mod h1:U0ZDG2WjWfnUWl7PiQo2+YphzlMteFj2RhjoBgyns0E= -github.com/aws/aws-sdk-go-v2/service/s3 v1.12.0/go.mod h1:6J++A5xpo7QDsIeSqPK4UHqMSyPOCopa+zKtqAMhqVQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.16.1/go.mod h1:CQe/KvWV1AqRc65KqeJjrLzr5X2ijnFTTVzJW0VBRCI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 h1:/EMdFPW/Ppieh0WUtQf1+qCGNLdsq5UWUyevBQ6vMVc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1/go.mod h1:/NHbqPRiwxSPVOB2Xr+StDEH+GWV/64WwnUjv4KYzV0= -github.com/aws/aws-sdk-go-v2/service/sso v1.3.2/go.mod h1:J21I6kF+d/6XHVk7kp/cx9YVD2TMD2TbLwtRGVcinXo= -github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 h1:GFZitO48N/7EsFDt8fMa5iYdmWqkUDDB3Eje6z3kbG0= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 h1:jcw6kKZrtNfBPJkaHrscDOZoe5gvi9wjudnxvozYFJo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= -github.com/aws/aws-sdk-go-v2/service/sts v1.6.1/go.mod h1:hLZ/AnkIKHLuPGjEiyghNEdvJ2PP0MgOxcmv9EBJ4xs= -github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 h1:KRAix/KHvjGODaHAMXnxRk9t0D+4IJVUuS/uwXxngXk= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= -github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1 h1:kl2z0sTngGlrfGqDDwOek573S2AJ6Ys+Wrf8I0b0B6A= -github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1/go.mod h1:VapwwZVNh07sUP9oTiH4Td+g5E6dCoR2bcnbTuwakJw= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blocto/solana-go-sdk v1.27.0 h1:nIsV0S0Hu7M0SktkgdDuTI/mM4FLyoInpu5M7wsl2W4= -github.com/blocto/solana-go-sdk v1.27.0/go.mod h1:Xoyhhb3hrGpEQ5rJps5a3OgMwDpmEhrd9bgzFKkkwMs= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= -github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/circonus-labs/circonusllhist v0.1.5 h1:ZSFhQTeulzPZW0V8reKXXQ3a2fa4Bld2X+aLUopckSw= -github.com/circonus-labs/circonusllhist v0.1.5/go.mod h1:qYdvAhMwBRcpDX02mwOjBk0EZltij0Q7ZmzZM0BzUaY= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhui/dktest v0.3.10 h1:0frpeeoM9pHouHjhLeZDuDTJ0PqjDTrycaHaMmkJAo8= -github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= -github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= -github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= -github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI= -github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= -github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= -github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 h1:wxgEEZvsnOTrDO2npSSKUMDx5IykfoGmro+/Vjc1BQ8= -github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= -github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc= -github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= -github.com/google/go-metrics-stackdriver v0.5.0 h1:2j/zXdGULmoTqEOcxfD05edfagkRRk7CAPiH8biQF3o= -github.com/google/go-metrics-stackdriver v0.5.0/go.mod h1:TSSNfaJwcAremxTHm6pe+uDwuT3u71QSyDwrRWAfPZg= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= -github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.5 h1:rOFDv+3k05mnW0oaDLffhVUwg03Csn0mvfO98Wdd2bE= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.5/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= -github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.4 h1:ws2CPDuXMKwaBb2z/duBCdnB9pSxlN2nuDZWXcVj6RU= -github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.4/go.mod h1:dDxt3GXi5QONVHYrJi2+EjsJLCUs59FktZQA8ZMnm+U= -github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 h1:ydUCtmr8f9F+mHZ1iCsvzqFTXqNVpewX3s9zcYipMKI= -github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1/go.mod h1:Sl/ffzV57UAyjtSg1h5Km0rN5+dtzZJm1CUztkoCW2c= -github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.1 h1:WxpTuafkDjdeeu0Xtk9y3m9YAJhfFMb8+y6eTnxvV8A= -github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.1/go.mod h1:3D5UB9fjot4oUTYGQ5gGmhLJKreyLZeI0XB+NxcLTKs= -github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.1 h1:6joKpqCFveaNMEwC3qna67usws6DjdxqfCuQEHSM0aM= -github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.1/go.mod h1:sDmsWR/W2LqwU217o32RzdHMb/FywGLF72PVIhpZ3hE= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.1 h1:+paf/3ompzaXe07BdxkV1vTnqvhwtmZPE4yQnMPTThI= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.1/go.mod h1:YRtkersQ2N3iHlPDG5B3xBQtBsNZ3bjmlCwnrl26jVE= -github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.0 h1:FnWV2E0NLj+yYdhToUQjU81ayCMgURiL2WbJ0V7u/XY= -github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.0/go.mod h1:17twrc0lM8IpfGqIv69WQvwgDiu3nRwWlk5YfCSQduY= -github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.1 h1:72zlIBTJd2pvYmINqotpvcI4ZXLxhRq2cVPTuqv0xqY= -github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.1/go.mod h1:JytRAxdJViV+unUUWedb7uzEy5pgu7OurbqX0eHEikE= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= -github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 h1:phcbL8urUzF/kxA/Oj6awENaRwfWsjP59GW7u2qlDyY= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= -github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/hcp-sdk-go v0.23.0 h1:3WarkQSK0VzxJaH6psHIGQagag3ujL+NjWagZZHpiZM= -github.com/hashicorp/hcp-sdk-go v0.23.0/go.mod h1:/9UoDY2FYYA8lFaKBb2HmM/jKYZGANmf65q9QRc/cVw= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault v1.12.7 h1:T+nWB2Ihe6xiNelLfC1BMJhV0dgJngDgRW8EiG6/em8= -github.com/hashicorp/vault v1.12.7/go.mod h1:TkP77qkpNyb7kXeZlLLsj0luGitsq5BzRtaBoXgSCs4= -github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI= -github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= -github.com/hashicorp/vault/sdk v0.6.1-0.20230427140652-b4b396ffc14f h1:0KmxboDYCgT0rssFOTOkqVkLGbueORiGpkfVA6r5LQs= -github.com/hashicorp/vault/sdk v0.6.1-0.20230427140652-b4b396ffc14f/go.mod h1:XduFY2J0HMoM4mt4kkxlrrkF8bYowzUc2Gog6epWVsA= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= -github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= -github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4= -github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linkedin/goavro v2.1.0+incompatible h1:DV2aUlj2xZiuxQyvag8Dy7zjY69ENjS66bWkSfdpddY= -github.com/linkedin/goavro v2.1.0+incompatible/go.mod h1:bBCwI2eGYpUI/4820s67MElg9tdeLbINjLjiM2xZFYM= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= -github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA= -github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578= -github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= -github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 h1:lFN7TVecCMbCHVNfEofDqqaVsuAlkFyDmmO7EF4nXj4= -github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454/go.mod h1:NeMochZp7jN/pYFuxLkrZtmLqbADmnp/y1+/dL+AsyQ= -github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/oracle/oci-go-sdk/v60 v60.0.0 h1:EJAWjEi4SY5Raha6iUzq4LTQ0uM5YFw/wat/L1ehIEM= -github.com/oracle/oci-go-sdk/v60 v60.0.0/go.mod h1:krz+2gkSzlSL/L4PvP0Z9pZpag9HYLNtsMd1PmxlA2w= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= -github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/segmentio/kafka-go v0.4.35 h1:TAsQ7q1SjS39PcFvU0zDJhCuVAxHomy7xOAfbdSuhzs= -github.com/segmentio/kafka-go v0.4.35/go.mod h1:GAjxBQJdQMB5zfNA21AhpaqOB2Mu+w3De4ni3Gbm8y0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= -github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= -github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= -github.com/square/go-jose v2.6.0+incompatible h1:tepXLy3u2yvfP+8pVutUL/WU19WuopWQy65iDrdDJDs= -github.com/square/go-jose v2.6.0+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stripe/stripe-go/v72 v72.122.0 h1:eRXWqnEwGny6dneQ5BsxGzUCED5n180u8n665JHlut8= -github.com/stripe/stripe-go/v72 v72.122.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5 h1:UOKr78K7smMjkKuwZjbOCzIFTUhIUzXc5+tYeaPRirE= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5/go.mod h1:asllDl3XCEQ1lFPD4Y1juAn5p35Zd4ghNGcDKN7U7dU= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= -github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= -github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA= -go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210818153620-00dd8d7831e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.79.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/linkedin/goavro.v1 v1.0.5 h1:BJa69CDh0awSsLUmZ9+BowBdokpduDZSM9Zk8oKHfN4= -gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= -gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= -gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= -modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= -modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= -modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= -modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= -modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= -modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/main/main.go b/main/main.go index 41a25095e..814e0c051 100644 --- a/main/main.go +++ b/main/main.go @@ -5,34 +5,12 @@ package main import ( // pull in tool module. setup code is in init - "github.com/brave-intl/bat-go/cmd" "github.com/brave-intl/bat-go/libs/logging" - _ "github.com/brave-intl/bat-go/tools/cmd" + "github.com/brave-intl/payments-service/cmd" - // pull in settlement module. setup code is in init - _ "github.com/brave-intl/bat-go/tools/settlement/cmd" - // pull in vault module. setup code is in init - _ "github.com/brave-intl/bat-go/tools/vault/cmd" - // pull in wallet module. setup code is in init - _ "github.com/brave-intl/bat-go/tools/wallet/cmd" - // pull in macaroon module. setup code is in init - _ "github.com/brave-intl/bat-go/tools/macaroon/cmd" - // pull in merchat module. setup code is in init - _ "github.com/brave-intl/bat-go/tools/merchant/cmd" - - // pull in rewards module. setup code is in init - _ "github.com/brave-intl/bat-go/services/rewards/cmd" - // pull in wallets module. setup code is in init - _ "github.com/brave-intl/bat-go/services/wallet/cmd" - // pull in serve module. setup code is in init - _ "github.com/brave-intl/bat-go/services/cmd" - // pull in ratios module. setup code is in init - _ "github.com/brave-intl/bat-go/services/ratios/cmd" - // pull in grants module. setup code is in init - _ "github.com/brave-intl/bat-go/services/grant/cmd" // pull in payments service - _ "github.com/brave-intl/bat-go/services/nitro" - _ "github.com/brave-intl/bat-go/services/payments/cmd" + _ "github.com/brave-intl/payments-service/services/nitro" + _ "github.com/brave-intl/payments-service/services/payments/cmd" ) var ( diff --git a/services/cmd/serve.go b/services/cmd/serve.go index 9007f7d4f..26980ee9d 100644 --- a/services/cmd/serve.go +++ b/services/cmd/serve.go @@ -5,8 +5,8 @@ import ( "os" "time" - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" + cmdutils "github.com/brave-intl/payments-service/cmd" + rootcmd "github.com/brave-intl/payments-service/cmd" appctx "github.com/brave-intl/bat-go/libs/context" "github.com/brave-intl/bat-go/libs/handlers" "github.com/brave-intl/bat-go/libs/logging" diff --git a/services/go.mod b/services/go.mod deleted file mode 100644 index 908041571..000000000 --- a/services/go.mod +++ /dev/null @@ -1,149 +0,0 @@ -module github.com/brave-intl/bat-go/services - -go 1.18 - -replace github.com/brave-intl/bat-go/cmd => ../cmd - -replace github.com/brave-intl/bat-go/libs => ../libs - -replace github.com/brave-intl/bat-go/tools => ../tools - -require ( - filippo.io/age v1.1.1 - github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/amazon-ion/ion-go v1.2.0 - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d - github.com/awa/go-iap v1.3.22 - github.com/aws/aws-sdk-go v1.44.206 - github.com/aws/aws-sdk-go-v2 v1.18.0 - github.com/aws/aws-sdk-go-v2/credentials v1.12.23 - github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 - github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6 - github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10 - github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 - github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 - github.com/aws/smithy-go v1.13.5 - github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1 - github.com/blocto/solana-go-sdk v1.27.0 - github.com/brave-intl/bat-go v1.0.2 - github.com/brave-intl/bat-go/libs v1.0.2 - github.com/brave-intl/bat-go/tools v1.0.2 - github.com/brave-intl/bat-go v0.1.2 - github.com/getsentry/sentry-go v0.14.0 - github.com/go-chi/chi v4.1.2+incompatible - github.com/go-chi/cors v1.2.1 - github.com/go-jose/go-jose/v3 v3.0.0 - github.com/go-playground/validator/v10 v10.11.1 - github.com/golang-migrate/migrate/v4 v4.15.2 - github.com/golang/mock v1.6.0 - github.com/gomodule/redigo v1.9.2 - github.com/google/uuid v1.3.0 - github.com/hashicorp/vault v1.12.7 - github.com/jarcoal/httpmock v1.3.0 - github.com/jmoiron/sqlx v1.3.5 - github.com/lib/pq v1.10.7 - github.com/linkedin/goavro v2.1.0+incompatible - github.com/mdlayher/vsock v1.2.0 - github.com/prometheus/client_golang v1.13.0 - github.com/rs/zerolog v1.28.0 - github.com/satori/go.uuid v1.2.0 - github.com/segmentio/kafka-go v0.4.35 - github.com/shopspring/decimal v1.3.1 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.13.0 - github.com/square/go-jose v2.6.0+incompatible - github.com/stretchr/testify v1.8.4 - github.com/stripe/stripe-go/v72 v72.122.0 - golang.org/x/crypto v0.14.0 - gopkg.in/macaroon.v2 v2.1.0 - gopkg.in/square/go-jose.v2 v2.6.0 -) - -require ( - cloud.google.com/go/compute v1.18.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - filippo.io/edwards25519 v1.0.0 // indirect - github.com/amzn/ion-go v1.1.3 // indirect - github.com/amzn/ion-hash-go v1.1.2 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/config v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcutil v1.0.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/klauspost/compress v1.15.15 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect - github.com/mssola/user_agent v0.5.3 // indirect - github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/pierrec/lz4/v4 v4.1.17 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/api v0.110.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/services/go.sum b/services/go.sum deleted file mode 100644 index a594cb95a..000000000 --- a/services/go.sum +++ /dev/null @@ -1,2357 +0,0 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/spanner v1.28.0/go.mod h1:7m6mtQZn/hMbMfx62ct5EWrGND4DNqkXyrmBPRS+OJo= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= -github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= -github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPEkMo= -github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a/go.mod h1:D73UAuEPckrDorYZdtlCu2ySOLuPB5W4rhIkmmc/XbI= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/amazon-ion/ion-go v1.2.0 h1:EgFy23/7gRxRYdUkJARh/7eZc8BYkFFDZZSqB3PwVqQ= -github.com/amazon-ion/ion-go v1.2.0/go.mod h1:3ZEje8i20TiIPVZlN+KE3B2ppZ1B8d9F/KaT7Dtec+k= -github.com/amzn/ion-go v1.1.3 h1:gGhjtLY0GUNQXej5N2qHhoVWQBkgtoPDt1feYYFMfOc= -github.com/amzn/ion-go v1.1.3/go.mod h1:7wQBWQ7PhPpZCr9PL+mtuIyNmyLjuV8qt2mrfxmvkA8= -github.com/amzn/ion-hash-go v1.1.2 h1:cUEolXoS7aPwYFknwae47zppF+gJgZEWqRiRbPdPIy8= -github.com/amzn/ion-hash-go v1.1.2/go.mod h1:6DKfguDnpHlHE8fHV7CxZiWnEudDxMDXUkSn2fu3j/4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= -github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apple/foundationdb/bindings/go v0.0.0-20190411004307-cd5c9d91fad2/go.mod h1:OMVSB21p9+xQUIqlGizHPZfjK+SHws1ht+ZytVDoz9U= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/awa/go-iap v1.3.22 h1:cfMplB/bwo9guOFblbaTme42VJ4hMUELduniM+pq4iM= -github.com/awa/go-iap v1.3.22/go.mod h1:DbAmBQTIePitXo8iqsPgsQNe9rLkp85SYk1XOEv3vxE= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.44.206 h1:xC7O40wdnKH4A95KdYt+smXl9hig1vu9b3mFxAxUoak= -github.com/aws/aws-sdk-go v1.44.206/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0= -github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= -github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 h1:RKci2D7tMwpvGpDNZnGQw9wk6v7o/xSwFcUAuNPoB8k= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9/go.mod h1:vCmV1q1VK8eoQJ5+aYE7PkK1K6v41qJ5pJdK3ggCDvg= -github.com/aws/aws-sdk-go-v2/config v1.6.0/go.mod h1:TNtBVmka80lRPk5+S9ZqVfFszOQAGJJ9KbT3EM3CHNU= -github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/config v1.17.10 h1:zBy5QQ/mkvHElM1rygHPAzuH+sl8nsdSaxSWj0+rpdE= -github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= -github.com/aws/aws-sdk-go-v2/credentials v1.3.2/go.mod h1:PACKuTJdt6AlXvEq8rFI4eDmoqDFC5DpVKQbWysaDgM= -github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23 h1:LctvcJMIb8pxvk5hQhChpCu0WlU6oKQmcYb1HA4IZSA= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23/go.mod h1:0awX9iRr/+UO7OwRQFpV1hNtXxOVuehpjVEzrIAYNcA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.4.0/go.mod h1:Mj/U8OpDbcVcoctrYwA2bak8k/HFPdcLzI/vaiXMwuM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 h1:E3PXZSI3F2bzyj6XxUXdTIfvp425HHhwKsFvmzBwHgs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.4.0/go.mod h1:eHwXu2+uE/T6gpnYWwBwqoeqRf9IXyCcolyOWDRAErQ= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.4/go.mod h1:Ex7XQmbFmgFHrjUX6TN3mApKW5Hglyga+F7wZHTtYhA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.0/go.mod h1:Q5jATQc+f1MfZp3PDMhn6ry18hGvE0i8yvbXoKbnZaE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 h1:Mza+vlnZr+fPKFKRq/lKGVvM6B/8ZZmNdEopOwSQLms= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 h1:2EXB7dtGwRYIN3XQ9qwIW504DVbKIw3r89xQnonGdsQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16/go.mod h1:XH+3h395e3WVdd6T2Z3mPxuI+x/HVtdqVOREkTiyubs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.2.2/go.mod h1:EASdTcM1lGhUe1/p4gkojHwlGJkeoRjjr1sRCzup3Is= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 h1:dpiPHgmFstgkLG07KaYAewvuptq5kvo52xn7tVSrtrQ= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10/go.mod h1:9cBNUHI2aW4ho0A5T87O294iPDuuUOSIEDjnd1Lq/z0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 h1:KSvtm1+fPXE0swe9GPjc6msyrdTT0LB/BP8eLugL1FI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20/go.mod h1:Mp4XI/CkWGD79AQxZ5lIFlgvC0A+gl+4BmyG1F+SfNc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.2/go.mod h1:NXmNI41bdEsJMrD0v9rUvbGCB5GwdBEpKvUvIY3vTFg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 h1:GE25AWCdNUPh9AOJzI9KIJnja7IwUc1WyUqz/JTyJ/I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.5.2/go.mod h1:QuL2Ym8BkrLmN4lUofXYq6000/i5jPjosCNK//t6gak= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.2/go.mod h1:np7TMuJNT83O0oDOSF8i4dF3dvGqA6hPYYo6YYkzgRA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 h1:piDBAaWkaxkkVV3xJJbTehXCZRXYs49kvpi/LG6LR2o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19/go.mod h1:BmQWRVkLTmyNzYPFAZgon53qKLWBNSvonugD1MrSWUs= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4= -github.com/aws/aws-sdk-go-v2/service/qldb v1.14.20/go.mod h1:9morR/lAo8ziBkYz5gxtGe0FzAjkcUfxAqSk0Q9obDc= -github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6 h1:BFK9rvyhv4OyxrZa0w4ItE2s5L2Zg/hMu9jA286YWvg= -github.com/aws/aws-sdk-go-v2/service/qldb v1.15.6/go.mod h1:+hUHf6G2dhZJcVUVNqqCmjezYNZu07akDOtc72upBEQ= -github.com/aws/aws-sdk-go-v2/service/qldbsession v1.13.19/go.mod h1:xX3iRpzN9iJYgP45OUYHJWFLrre0/s1Mur8SjOwg3RU= -github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10 h1:xZuYRwfZ1nFBWQVjXaegmvZcqsfsbzDYyXxXXdujKN4= -github.com/aws/aws-sdk-go-v2/service/qldbsession v1.14.10/go.mod h1:U0ZDG2WjWfnUWl7PiQo2+YphzlMteFj2RhjoBgyns0E= -github.com/aws/aws-sdk-go-v2/service/s3 v1.12.0/go.mod h1:6J++A5xpo7QDsIeSqPK4UHqMSyPOCopa+zKtqAMhqVQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.16.1/go.mod h1:CQe/KvWV1AqRc65KqeJjrLzr5X2ijnFTTVzJW0VBRCI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 h1:/EMdFPW/Ppieh0WUtQf1+qCGNLdsq5UWUyevBQ6vMVc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1/go.mod h1:/NHbqPRiwxSPVOB2Xr+StDEH+GWV/64WwnUjv4KYzV0= -github.com/aws/aws-sdk-go-v2/service/sso v1.3.2/go.mod h1:J21I6kF+d/6XHVk7kp/cx9YVD2TMD2TbLwtRGVcinXo= -github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 h1:GFZitO48N/7EsFDt8fMa5iYdmWqkUDDB3Eje6z3kbG0= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 h1:jcw6kKZrtNfBPJkaHrscDOZoe5gvi9wjudnxvozYFJo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= -github.com/aws/aws-sdk-go-v2/service/sts v1.6.1/go.mod h1:hLZ/AnkIKHLuPGjEiyghNEdvJ2PP0MgOxcmv9EBJ4xs= -github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 h1:KRAix/KHvjGODaHAMXnxRk9t0D+4IJVUuS/uwXxngXk= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= -github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1 h1:kl2z0sTngGlrfGqDDwOek573S2AJ6Ys+Wrf8I0b0B6A= -github.com/awslabs/amazon-qldb-driver-go/v3 v3.0.1/go.mod h1:VapwwZVNh07sUP9oTiH4Td+g5E6dCoR2bcnbTuwakJw= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blocto/solana-go-sdk v1.27.0 h1:nIsV0S0Hu7M0SktkgdDuTI/mM4FLyoInpu5M7wsl2W4= -github.com/blocto/solana-go-sdk v1.27.0/go.mod h1:Xoyhhb3hrGpEQ5rJps5a3OgMwDpmEhrd9bgzFKkkwMs= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/brave-intl/bat-go v0.1.2 h1:5b0JJ6gbeMi1uvZdVvc+EyAbWCt4adaj7TzmBEhuihs= -github.com/brave-intl/bat-go v0.1.2/go.mod h1:U2qMRZG22UsCkBZkGf+0wjEmKvH8SYH9l5gBRou4w0s= -github.com/brave-intl/bat-go v1.0.2 h1:0JHIPQrs3PuPL0BdUBOdlEImN7IsSZ3J5pLHr2wPCxA= -github.com/brave-intl/bat-go v1.0.2/go.mod h1:v0+eoO+LrHWe9+gcunYvB+t5mXxPRY1RVR3ZoVKZuC4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20190410005359-59a85de7f35e/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0/go.mod h1:5d8DqS60xkj9k3aXfL3+mXBH0DPYO0FQjcKosxl+b/Q= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.2.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= -github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= -github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= -github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4= -github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE= -github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= -github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= -github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= -github.com/dhui/dktest v0.3.1/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= -github.com/dhui/dktest v0.3.10 h1:0frpeeoM9pHouHjhLeZDuDTJ0PqjDTrycaHaMmkJAo8= -github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0= -github.com/digitalocean/godo v1.7.5/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/ethereum/go-ethereum v1.7.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= -github.com/frankban/quicktest v1.4.2/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= -github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7/go.mod h1:GeIq9qoE43YdGnDXURnmKTnGg15pQz4mYkXSTChbneI= -github.com/gammazero/workerpool v0.0.0-20190406235159-88d534f22b56/go.mod h1:w9RqFVO2BM3xwWEcAB8Fwp0OviTBBEiRmSBDfbXnd3w= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3/go.mod h1:ZXFhGda43Z2TVbfGZefXyMJzsDHhCh0go3bZUcwTx7o= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= -github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0= -github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc= -github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= -github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= -github.com/google/go-metrics-stackdriver v0.2.0/go.mod h1:KLcPyp3dWJAFD+yHisGlJSZktIsTjb50eB72U2YZ9K0= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.0.0/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod h1:xbXnmKqX9/+RhPkJ4zrEx4738HacP72aaUPlT2RZ4sU= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= -github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c/go.mod h1:kiPs9g148eLShc2TYagUAyKDnD+dH9U+CQKsXzlY9xo= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault v1.1.1/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= -github.com/hashicorp/vault v1.12.7 h1:T+nWB2Ihe6xiNelLfC1BMJhV0dgJngDgRW8EiG6/em8= -github.com/hashicorp/vault v1.12.7/go.mod h1:TkP77qkpNyb7kXeZlLLsj0luGitsq5BzRtaBoXgSCs4= -github.com/hashicorp/vault-plugin-mock v0.16.1/go.mod h1:83G4JKlOwUtxVourn5euQfze3ZWyXcUiLj2wqrKSDIM= -github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= -github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= -github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jeffchao/backoff v0.0.0-20140404060208-9d7fd7aa17f2/go.mod h1:xkfESuHriIekR+4RoV+fu91j/CfnYM29Zi2tMFw5iD4= -github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00= -github.com/jefferai/jsonx v1.0.0/go.mod h1:OGmqmi2tTeI/PS+qQfBDToLHHJIy/RMp24fPo8vFvoQ= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= -github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linkedin/goavro v2.1.0+incompatible h1:DV2aUlj2xZiuxQyvag8Dy7zjY69ENjS66bWkSfdpddY= -github.com/linkedin/goavro v2.1.0+incompatible/go.mod h1:bBCwI2eGYpUI/4820s67MElg9tdeLbINjLjiM2xZFYM= -github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= -github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578= -github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc/go.mod h1:1rLVY/DWf3U6vSZgH16S7pymfrhK2lcUlXjgGglw/lY= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 h1:lFN7TVecCMbCHVNfEofDqqaVsuAlkFyDmmO7EF4nXj4= -github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454/go.mod h1:NeMochZp7jN/pYFuxLkrZtmLqbADmnp/y1+/dL+AsyQ= -github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= -github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/openlyinc/pointy v1.1.2/go.mod h1:w2Sytx+0FVuMKn37xpXIAyBNhFNBIJGR/v2m7ik1WtM= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/oracle/oci-go-sdk v13.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= -github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/pquerna/otp v1.2.1-0.20191009055518-468c2dd2b58d/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/pressly/lg v1.1.1/go.mod h1:B/l4UikoXw0H/DXW1O0BJxa1vVU106gryElqMy1+NDw= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rboyer/safeio v0.2.1/go.mod h1:Cq/cEPK+YXFn622lsQ0K4KsPZSPtaptHHEldsy7Fmig= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.14.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/segmentio/kafka-go v0.4.35 h1:TAsQ7q1SjS39PcFvU0zDJhCuVAxHomy7xOAfbdSuhzs= -github.com/segmentio/kafka-go v0.4.35/go.mod h1:GAjxBQJdQMB5zfNA21AhpaqOB2Mu+w3De4ni3Gbm8y0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20190905144223-a36b5d85f337/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= -github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= -github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= -github.com/square/go-jose v2.6.0+incompatible h1:tepXLy3u2yvfP+8pVutUL/WU19WuopWQy65iDrdDJDs= -github.com/square/go-jose v2.6.0+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stripe/stripe-go/v72 v72.122.0 h1:eRXWqnEwGny6dneQ5BsxGzUCED5n180u8n665JHlut8= -github.com/stripe/stripe-go/v72 v72.122.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5/go.mod h1:asllDl3XCEQ1lFPD4Y1juAn5p35Zd4ghNGcDKN7U7dU= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip39 v0.0.0-20180716170310-95c66720ed7a/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= -github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= -github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190909082730-f460065e899a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210818153620-00dd8d7831e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190905072037-92dd089d5514/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= -gopkg.in/linkedin/goavro.v1 v1.0.5 h1:BJa69CDh0awSsLUmZ9+BowBdokpduDZSM9Zk8oKHfN4= -gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= -gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= -gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= -gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -layeh.com/radius v0.0.0-20190322222518-890bc1058917/go.mod h1:fywZKyu//X7iRzaxLgPWsvc0L26IUpVvE/aeIL2JtIQ= -modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= -modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= -modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= -modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= -modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= -modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= -modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= -mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/services/grant/cmd/grant.go b/services/grant/cmd/grant.go deleted file mode 100644 index 56cc51470..000000000 --- a/services/grant/cmd/grant.go +++ /dev/null @@ -1,724 +0,0 @@ -package cmd - -import ( - "context" - "encoding/base64" - "fmt" - "net/http" - _ "net/http/pprof" // Enable profiling. - "os" - "strconv" - "strings" - "time" - - "github.com/asaskevich/govalidator" - "github.com/getsentry/sentry-go" - "github.com/go-chi/chi" - chiware "github.com/go-chi/chi/middleware" - "github.com/rs/zerolog" - "github.com/rs/zerolog/hlog" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/brave-intl/bat-go/cmd" - cmdutils "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/clients/reputation" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - srv "github.com/brave-intl/bat-go/libs/service" - servicescmd "github.com/brave-intl/bat-go/services/cmd" - "github.com/brave-intl/bat-go/services/grant" - "github.com/brave-intl/bat-go/services/promotion" - "github.com/brave-intl/bat-go/services/skus" - "github.com/brave-intl/bat-go/services/skus/handler" - "github.com/brave-intl/bat-go/services/skus/storage/repository" - "github.com/brave-intl/bat-go/services/wallet" - _ "github.com/brave-intl/bat-go/services/wallet/cmd" // Reuse Wallet env variables bound by Viper bind-env. -) - -var ( - // GrantServerCmd start up the grant server - GrantServerCmd = &cobra.Command{ - Use: "grant", - Short: "subcommand to start up grant server", - Run: cmd.Perform("grant", RunGrantServer), - } -) - -func init() { - servicescmd.ServeCmd.AddCommand(GrantServerCmd) - - flagBuilder := cmdutils.NewFlagBuilder(GrantServerCmd) - - flagBuilder.Flag().Bool("require-uphold-destination-country", false, - "require responses for linkings to uphold to contain country identity information"). - Bind("require-uphold-destination-country"). - Env("REQUIRE_UPHOLD_DESTINATION_COUNTRY") - - flagBuilder.Flag().Bool("enable-job-workers", true, - "enable job workers (defaults true)"). - Bind("enable-job-workers"). - Env("ENABLE_JOB_WORKERS") - - flagBuilder.Flag().Bool("disable-disconnect", false, - "disable custodian ability to disconnect rewards wallets"). - Bind("disable-disconnect"). - Env("DISABLE_DISCONNECT") - - flagBuilder.Flag().Bool("disable-uphold-linking", false, - "disable custodian linking to uphold"). - Bind("disable-uphold-linking"). - Env("DISABLE_UPHOLD_LINKING") - - flagBuilder.Flag().Bool("disable-gemini-linking", false, - "disable custodian linking to gemini"). - Bind("disable-gemini-linking"). - Env("DISABLE_GEMINI_LINKING") - - flagBuilder.Flag().Bool("disable-bitflyer-linking", false, - "disable custodian linking to bitflyer"). - Bind("disable-bitflyer-linking"). - Env("DISABLE_BITFLYER_LINKING") - - flagBuilder.Flag().Bool("disable-zebpay-linking", false, - "disable custodial linking for zebpay"). - Bind("disable-zebpay-linking"). - Env("DISABLE_ZEBPAY_LINKING") - - flagBuilder.Flag().StringSlice("brave-transfer-promotion-ids", []string{""}, - "brave vg deposit destination promotion id"). - Bind("brave-transfer-promotion-ids"). - Env("BRAVE_TRANSFER_PROMOTION_IDS") - - flagBuilder.Flag().StringSlice("skus-whitelist", []string{""}, - "the whitelist of skus"). - Bind("skus-whitelist"). - Env("SKUS_WHITELIST") - - flagBuilder.Flag().StringSlice("country-blacklist", []string{""}, - "the blacklist of countries for wallet linking"). - Bind("country-blacklist"). - Env("COUNTRY_BLACKLIST") - - flagBuilder.Flag().String("merge-param-bucket", "", - "parameters for the application"). - Bind("merge-param-bucket"). - Env("MERGE_PARAM_BUCKET") - - flagBuilder.Flag().String("disabled-wallet-geo-countries", "disabled-wallet-geo-countries.json", - "the json file containing disabled geo countries for wallet creation"). - Env("DISABLED_WALLET_GEO_COUNTRIES"). - Bind("disabled-wallet-geo-countries") - - flagBuilder.Flag().String("wallet-on-platform-prior-to", "", - "wallet on platform prior to for transfer"). - Bind("wallet-on-platform-prior-to"). - Env("WALLET_ON_PLATFORM_PRIOR_TO") - - flagBuilder.Flag().Bool("reputation-on-drain", false, - "check wallet reputation on drain"). - Bind("reputation-on-drain"). - Env("REPUTATION_ON_DRAIN") - - flagBuilder.Flag().Bool("use-custodian-regions", false, - "use custodian regions for figuring block on country for linking"). - Bind("use-custodian-regions"). - Env("USE_CUSTODIAN_REGIONS") - - flagBuilder.Flag().Bool("reputation-withdrawal-on-drain", false, - "check wallet withdrawal reputation on drain"). - Bind("reputation-withdrawal-on-drain"). - Env("REPUTATION_WITHDRAWAL_ON_DRAIN") - - // Configuration for Radom. - flagBuilder.Flag().Bool( - "radom-enabled", - false, - "is radom enabled for skus", - ).Bind("radom-enabled").Env("RADOM_ENABLED") - - flagBuilder.Flag().String( - "radom-seller-address", - "", - "the seller address for radom", - ).Bind("radom-seller-address").Env("RADOM_SELLER_ADDRESS") - - flagBuilder.Flag().String( - "radom-server", - "", - "the server address for radom", - ).Bind("radom-server").Env("RADOM_SERVER") - - flagBuilder.Flag().String( - "radom-secret", - "", - "the server token for radom", - ).Bind("radom-secret").Env("RADOM_SECRET") - - flagBuilder.Flag().String( - "radom-webhook-secret", - "", - "the server webhook secret for radom", - ).Bind("radom-webhook-secret").Env("RADOM_WEBHOOK_SECRET") - - // stripe configurations - flagBuilder.Flag().Bool("stripe-enabled", false, - "is stripe enabled for skus"). - Bind("stripe-enabled"). - Env("STRIPE_ENABLED") - - flagBuilder.Flag().String("stripe-webhook-secret", "", - "the stripe webhook secret"). - Bind("stripe-webhook-secret"). - Env("STRIPE_WEBHOOK_SECRET") - - flagBuilder.Flag().String("stripe-secret", "", - "the stripe secret"). - Bind("stripe-secret"). - Env("STRIPE_SECRET") - - // gemini skus credentials - flagBuilder.Flag().String("skus-gemini-settlement-address", "", - "the settlement address for skus gemini"). - Bind("skus-gemini-settlement-address"). - Env("SKUS_GEMINI_SETTLEMENT_ADDRESS") - - flagBuilder.Flag().String("skus-gemini-api-key", "", - "the api key for skus gemini"). - Bind("skus-gemini-api-key"). - Env("SKUS_GEMINI_API_KEY") - - flagBuilder.Flag().String("skus-gemini-api-secret", "", - "the api secret for skus gemini"). - Bind("skus-gemini-api-secret"). - Env("SKUS_GEMINI_API_SECRET") - - flagBuilder.Flag().String("skus-gemini-browser-client-id", "", - "the browser client id for gemini, which is the oauth client id the browser uses, required to validate transactions for AC flow"). - Bind("skus-gemini-browser-client-id"). - Env("SKUS_GEMINI_BROWSER_CLIENT_ID") - - flagBuilder.Flag().String("skus-gemini-client-id", "", - "the client id for skus gemini"). - Bind("skus-gemini-client-id"). - Env("SKUS_GEMINI_CLIENT_ID") - - flagBuilder.Flag().String("skus-gemini-client-secret", "", - "the client secret for skus gemini"). - Bind("skus-gemini-client-secret"). - Env("SKUS_GEMINI_CLIENT_SECRET") - - // gemini credentials - flagBuilder.Flag().String("gemini-settlement-address", "", - "the settlement address for gemini"). - Bind("gemini-settlement-address"). - Env("GEMINI_SETTLEMENT_ADDRESS") - - flagBuilder.Flag().String("gemini-api-key", "", - "the api key for gemini"). - Bind("gemini-api-key"). - Env("GEMINI_API_KEY") - - flagBuilder.Flag().String("gemini-api-secret", "", - "the api secret for gemini"). - Bind("gemini-api-secret"). - Env("GEMINI_API_SECRET") - - flagBuilder.Flag().String("gemini-browser-client-id", "", - "the browser client id for gemini, which is the oauth client id the browser uses, required to validate transactions for AC flow"). - Bind("gemini-browser-client-id"). - Env("GEMINI_BROWSER_CLIENT_ID") - - flagBuilder.Flag().String("gemini-client-id", "", - "the client id for gemini"). - Bind("gemini-client-id"). - Env("GEMINI_CLIENT_ID") - - flagBuilder.Flag().String("gemini-client-secret", "", - "the client secret for gemini"). - Bind("gemini-client-secret"). - Env("GEMINI_CLIENT_SECRET") - - flagBuilder.Flag().String("zebpay-linking-key", "", - "the linking key for zebpay custodian"). - Bind("zebpay-linking-key"). - Env("ZEBPAY_LINKING_KEY") - - // bitflyer credentials - flagBuilder.Flag().String("bitflyer-client-id", "", - "tells bitflyer what the client id is during token generation"). - Bind("bitflyer-client-id"). - Env("BITFLYER_CLIENT_ID") - - flagBuilder.Flag().String("bitflyer-client-secret", "", - "tells bitflyer what the client secret during token generation"). - Bind("bitflyer-client-secret"). - Env("BITFLYER_CLIENT_SECRET") - - flagBuilder.Flag().String("bitflyer-extra-client-secret", "", - "tells bitflyer what the extra client secret is during token"). - Bind("bitflyer-extra-client-secret"). - Env("BITFLYER_EXTRA_CLIENT_SECRET") - - flagBuilder.Flag().String("bitflyer-server", "", - "the bitflyer domain to interact with"). - Bind("bitflyer-server"). - Env("BITFLYER_SERVER") - - flagBuilder.Flag().String("unlinking-cooldown", "", - "the cooldown period for custodial wallet unlinking"). - Bind("unlinking-cooldown"). - Env("UNLINKING_COOLDOWN") - - // playstore json key - flagBuilder.Flag().String("playstore-json-key", "", - "the playstore json key"). - Bind("playstore-json-key"). - Env("PLAYSTORE_JSON_KEY") - - // appstore key - flagBuilder.Flag().String("apple-receipt-shared-key", "", - "the appstore shared key"). - Bind("apple-receipt-shared-key"). - Env("APPLE_RECEIPT_SHARED_KEY") - - flagBuilder.Flag().Bool("enable-store-signed-order-creds-consumer", true, - "enable store signed order creds consumer"). - Bind("enable-store-signed-order-creds-consumer"). - Env("ENABLE_STORE_SIGNED_ORDER_CREDS_CONSUMER") - - flagBuilder.Flag().Int("number-store-signed-order-creds-consumer", 1, - "number of consumers to create for store signed order creds"). - Bind("number-store-signed-order-creds-consumer"). - Env("NUMBER_STORE_SIGNED_ORDER_CREDS_CONSUMER") - - flagBuilder.Flag().String("kafka-brokers", "", - "kafka broker list"). - Bind("kafka-brokers"). - Env("KAFKA_BROKERS") -} - -func setupRouter(ctx context.Context, logger *zerolog.Logger) (context.Context, *chi.Mux, *promotion.Service, []srv.Job) { - buildTime, _ := ctx.Value(appctx.BuildTimeCTXKey).(string) - commit, _ := ctx.Value(appctx.CommitCTXKey).(string) - version, _ := ctx.Value(appctx.VersionCTXKey).(string) - env, _ := ctx.Value(appctx.EnvironmentCTXKey).(string) - - // runnable jobs for the services created - jobs := []srv.Job{} - - govalidator.SetFieldsRequiredByDefault(true) - - r := chi.NewRouter() - - // chain should be: - // id / transfer -> ip -> heartbeat -> request logger / recovery -> token check -> rate limit - // -> instrumentation -> handler - r.Use(chiware.RequestID) - r.Use(middleware.RequestIDTransfer) - r.Use(middleware.HostTransfer) - - // NOTE: This uses standard forwarding headers, note that this puts implicit trust in the header values - // provided to us. In particular, it uses the first element. - // Consequently, we should consider the request IP as primarily "informational". - r.Use(chiware.RealIP) - - r.Use(chiware.Heartbeat("/")) - // log and recover here - if logger != nil { - // Also handles panic recovery - r.Use(hlog.NewHandler(*logger)) - r.Use(hlog.UserAgentHandler("user_agent")) - r.Use(hlog.RequestIDHandler("req_id", "Request-Id")) - r.Use(middleware.RequestLogger(logger)) - } - // now we have middlewares we want included in logging - r.Use(chiware.Timeout(15 * time.Second)) - r.Use(middleware.BearerToken) - if os.Getenv("ENV") == "production" { - // allow a burst of 4 - ctx = context.WithValue(ctx, appctx.RateLimiterBurstCTXKey, 4) - // one request (or burst) every 500 ms - r.Use(middleware.RateLimiter(ctx, 120)) - } - - var walletService *wallet.Service - // use cobra configurations for setting up wallet service - // this way we can have the wallet service completely separated from - // grants service and easily deployable. - ctx, walletService = wallet.SetupService(ctx) - r = wallet.RegisterRoutes(ctx, walletService, r) - - promotionDB, promotionRODB, err := promotion.NewPostgres() - if err != nil { - logger.Panic().Err(err).Msg("unable connect to promotion db") - } - - promotionService, err := promotion.InitService( - ctx, - promotionDB, - promotionRODB, - walletService, - ) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("Promotion service initialization failed") - } - - grantDB, grantRODB, err := grant.NewPostgres() - if err != nil { - logger.Panic().Err(err).Msg("unable connect to grant db") - } - - grantService, err := grant.InitService( - ctx, - grantDB, - grantRODB, - walletService, - promotionService, - ) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("Grant service initialization failed") - } - - // add runnable jobs: - jobs = append(jobs, grantService.Jobs()...) - - // add runnable jobs: - jobs = append(jobs, promotionService.Jobs()...) - - // vbat expired var from env - vbatExpires, err := time.Parse(time.RFC3339, "2023-11-02T00:00:00Z") // default 11/2/23 - if err != nil { - logger.Panic().Err(err).Msg("failed to parse vbatExpires time") - } - if os.Getenv("VBAT_EXPIRES") != "" { // use what is in the environment if exists - vbatExpires, err = time.Parse(time.RFC3339, os.Getenv("VBAT_EXPIRES")) - if err != nil { - logger.Panic().Err(err).Msg("failed to parse vbatExpires time") - } - } - - r.Mount("/v1/promotions", promotion.Router(promotionService, vbatExpires)) - r.Mount("/v2/promotions", promotion.RouterV2(promotionService, vbatExpires)) - - sRouter, err := promotion.SuggestionsRouter(promotionService, vbatExpires) - if err != nil { - logger.Panic().Err(err).Msg("failed to initialize the suggestions router") - } - - r.Mount("/v1/suggestions", sRouter) - - sV2Router, err := promotion.SuggestionsV2Router(promotionService, vbatExpires) - if err != nil { - logger.Panic().Err(err).Msg("failed to initialize the suggestions router") - } - - r.Mount("/v2/suggestions", sV2Router) - - // temporarily house batloss events in promotion to avoid widespread conflicts later - r.Mount("/v1/wallets", promotion.WalletEventRouter(promotionService, vbatExpires)) - - skuOrderRepo := repository.NewOrder() - skuOrderItemRepo := repository.NewOrderItem() - skuOrderPayHistRepo := repository.NewOrderPayHistory() - skuIssuerRepo := repository.NewIssuer() - - skusPG, err := skus.NewPostgres( - skuOrderRepo, - skuOrderItemRepo, - skuOrderPayHistRepo, - skuIssuerRepo, - "", true, "skus_db", - ) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("Must be able to init postgres connection to start") - } - - // skus gemini variables - skuCtx := context.WithValue(ctx, appctx.GeminiSettlementAddressCTXKey, viper.GetString("skus-gemini-settlement-address")) - skuCtx = context.WithValue(skuCtx, appctx.GeminiAPIKeyCTXKey, viper.GetString("skus-gemini-api-key")) - skuCtx = context.WithValue(skuCtx, appctx.GeminiAPISecretCTXKey, viper.GetString("skus-gemini-api-secret")) - skuCtx = context.WithValue(skuCtx, appctx.GeminiBrowserClientIDCTXKey, viper.GetString("skus-gemini-browser-client-id")) - skuCtx = context.WithValue(skuCtx, appctx.GeminiClientIDCTXKey, viper.GetString("skus-gemini-client-id")) - skuCtx = context.WithValue(skuCtx, appctx.GeminiClientSecretCTXKey, viper.GetString("skus-gemini-client-secret")) - - skusService, err := skus.InitService(skuCtx, skusPG, walletService, skuOrderRepo, skuIssuerRepo) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("SKUs service initialization failed") - } - - // add runnable jobs: - jobs = append(jobs, skusService.Jobs()...) - - // initialize skus service keys for credentials to use - skus.InitEncryptionKeys() - - { - origins := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",") - dbg, _ := strconv.ParseBool(os.Getenv("DEBUG")) - corsOpts := skus.NewCORSOpts(origins, dbg) - - authMwr := skus.NewAuthMwr(skusService) - - r.Mount("/v1/credentials", skus.CredentialRouter(skusService, authMwr)) - r.Mount("/v2/credentials", skus.CredentialV2Router(skusService, authMwr)) - r.Mount("/v1/orders", skus.Router(skusService, authMwr, middleware.InstrumentHandler, corsOpts)) - - subr := chi.NewRouter() - orderh := handler.NewOrder(skusService) - - if os.Getenv("ENV") == "local" { - corsMwrPost := skus.NewCORSMwr(corsOpts, http.MethodPost) - - subr.Method( - http.MethodOptions, - "/", - middleware.InstrumentHandler("CreateOrderNewOptions", corsMwrPost(nil)), - ) - - subr.Method( - http.MethodPost, - "/", - middleware.InstrumentHandler( - "CreateOrderNew", - corsMwrPost(handlers.AppHandler(orderh.Create)), - ), - ) - } else { - subr.Method( - http.MethodPost, - "/", - middleware.InstrumentHandler("CreateOrderNew", authMwr(handlers.AppHandler(orderh.CreateNew))), - ) - } - - r.Mount("/v1/orders-new", subr) - } - - r.Mount("/v1/webhooks", skus.WebhookRouter(skusService)) - r.Mount("/v1/votes", skus.VoteRouter(skusService, middleware.InstrumentHandler)) - - // add profiling flag to enable profiling routes - if os.Getenv("PPROF_ENABLED") != "" { - // pprof attaches routes to default serve mux - // host:6061/debug/pprof/ - go func() { - log.Error().Err(http.ListenAndServe(":6061", http.DefaultServeMux)) - }() - } - - logger.Info(). - Str("version", version). - Str("commit", commit). - Str("buildTime", buildTime). - Msg("server starting up") - - { - status := newSrvStatusFromCtx(ctx) - r.Get("/health-check", handlers.HealthCheckHandler(version, buildTime, commit, status, nil)) - } - - reputationServer := os.Getenv("REPUTATION_SERVER") - reputationToken := os.Getenv("REPUTATION_TOKEN") - if len(reputationServer) == 0 { - if env != "local" { - logger.Panic().Msg("REPUTATION_SERVER is missing in production environment") - } - } else { - proxyRouter := reputation.ProxyRouter(reputationServer, reputationToken) - r.Mount("/v1/devicecheck", proxyRouter) - r.Mount("/v1/captchas", proxyRouter) - r.Mount("/v2/attestations/safetynet", proxyRouter) - r.Mount("/v1/attestations/android", proxyRouter) - // v3/captcha - r.Mount("/v3/captcha", proxyRouter) - } - - return ctx, r, promotionService, jobs -} - -// RunGrantServer is the runner for starting up the grant server -func RunGrantServer(cmd *cobra.Command, args []string) error { - enableJobWorkers, err := cmd.Flags().GetBool("enable-job-workers") - if err != nil { - return err - } - ctx := cmd.Context() - return GrantServer( - ctx, - enableJobWorkers, - ) -} - -// GrantServer runs the grant server -func GrantServer( - ctx context.Context, - enableJobWorkers bool, -) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - ctx, logger = logging.SetupLogger(ctx) - } - - sentryDsn := os.Getenv("SENTRY_DSN") - if sentryDsn != "" { - buildTime := ctx.Value(appctx.BuildTimeCTXKey).(string) - commit := ctx.Value(appctx.CommitCTXKey).(string) - err := sentry.Init(sentry.ClientOptions{ - Dsn: sentryDsn, - Release: fmt.Sprintf("bat-go@%s-%s", commit, buildTime), - }) - defer sentry.Flush(2 * time.Second) - if err != nil { - logger.Panic().Err(err).Msg("unable to setup reporting!") - } - } - logger.Info(). - Str("prefix", "main"). - Msg("Starting server") - - // add flags to context - ctx = context.WithValue(ctx, appctx.KafkaBrokersCTXKey, viper.GetString("kafka-brokers")) - ctx = context.WithValue(ctx, appctx.BraveTransferPromotionIDCTXKey, viper.GetStringSlice("brave-transfer-promotion-ids")) - ctx = context.WithValue(ctx, appctx.WalletOnPlatformPriorToCTXKey, viper.GetString("wallet-on-platform-prior-to")) - ctx = context.WithValue(ctx, appctx.ReputationOnDrainCTXKey, viper.GetBool("reputation-on-drain")) - ctx = context.WithValue(ctx, appctx.UseCustodianRegionsCTXKey, viper.GetBool("use-custodian-regions")) - ctx = context.WithValue(ctx, appctx.ReputationWithdrawalOnDrainCTXKey, viper.GetBool("reputation-withdrawal-on-drain")) - // disable-disconnect wallet apis - ctx = context.WithValue(ctx, appctx.DisableDisconnectCTXKey, viper.GetBool("disable-disconnect")) - - // bitflyer variables - ctx = context.WithValue(ctx, appctx.BitflyerExtraClientSecretCTXKey, viper.GetString("bitflyer-extra-client-secret")) - ctx = context.WithValue(ctx, appctx.BitflyerClientSecretCTXKey, viper.GetString("bitflyer-client-secret")) - ctx = context.WithValue(ctx, appctx.BitflyerClientIDCTXKey, viper.GetString("bitflyer-client-id")) - - // gemini variables - ctx = context.WithValue(ctx, appctx.GeminiSettlementAddressCTXKey, viper.GetString("gemini-settlement-address")) - ctx = context.WithValue(ctx, appctx.GeminiAPIKeyCTXKey, viper.GetString("gemini-api-key")) - ctx = context.WithValue(ctx, appctx.GeminiAPISecretCTXKey, viper.GetString("gemini-api-secret")) - ctx = context.WithValue(ctx, appctx.GeminiBrowserClientIDCTXKey, viper.GetString("gemini-browser-client-id")) - ctx = context.WithValue(ctx, appctx.GeminiClientIDCTXKey, viper.GetString("gemini-client-id")) - ctx = context.WithValue(ctx, appctx.GeminiClientSecretCTXKey, viper.GetString("gemini-client-secret")) - - // zebpay wallet linking signing key - ctx = context.WithValue(ctx, appctx.ZebPayLinkingKeyCTXKey, viper.GetString("zebpay-linking-key")) - - // linking variables - ctx = context.WithValue(ctx, appctx.DisableZebPayLinkingCTXKey, viper.GetBool("disable-zebpay-linking")) - ctx = context.WithValue(ctx, appctx.DisableUpholdLinkingCTXKey, viper.GetBool("disable-uphold-linking")) - ctx = context.WithValue(ctx, appctx.DisableGeminiLinkingCTXKey, viper.GetBool("disable-gemini-linking")) - ctx = context.WithValue(ctx, appctx.DisableBitflyerLinkingCTXKey, viper.GetBool("disable-bitflyer-linking")) - - // stripe variables - ctx = context.WithValue(ctx, appctx.StripeEnabledCTXKey, viper.GetBool("stripe-enabled")) - ctx = context.WithValue(ctx, appctx.StripeWebhookSecretCTXKey, viper.GetString("stripe-webhook-secret")) - ctx = context.WithValue(ctx, appctx.StripeSecretCTXKey, viper.GetString("stripe-secret")) - - // Variables for Radom. - ctx = context.WithValue(ctx, appctx.RadomEnabledCTXKey, viper.GetBool("radom-enabled")) - ctx = context.WithValue(ctx, appctx.RadomWebhookSecretCTXKey, viper.GetString("radom-webhook-secret")) - ctx = context.WithValue(ctx, appctx.RadomSecretCTXKey, viper.GetString("radom-secret")) - ctx = context.WithValue(ctx, appctx.RadomServerCTXKey, viper.GetString("radom-server")) - ctx = context.WithValue(ctx, appctx.RadomSellerAddressCTXKey, viper.GetString("radom-seller-address")) - - // require country present from uphold txs - ctx = context.WithValue(ctx, appctx.RequireUpholdCountryCTXKey, viper.GetBool("require-uphold-destination-country")) - - // whitelisted skus - ctx = context.WithValue(ctx, appctx.WhitelistSKUsCTXKey, viper.GetStringSlice("skus-whitelist")) - - // the bucket for the custodian regions - ctx = context.WithValue(ctx, appctx.ParametersMergeBucketCTXKey, viper.Get("merge-param-bucket")) - - // the json file containing disabled wallet geo countries. - ctx = context.WithValue(ctx, appctx.DisabledWalletGeoCountriesCTXKey, viper.Get("disabled-wallet-geo-countries")) - - // blacklisted countries - ctx = context.WithValue(ctx, appctx.BlacklistedCountryCodesCTXKey, viper.GetStringSlice("country-blacklist")) - - // pull down from s3 the appropriate values for geo allow/block rules - - // custodian unlinking cooldown - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, viper.GetString("unlinking-cooldown")) - - // skus enable store signed order creds consumer - ctx = context.WithValue(ctx, appctx.SkusEnableStoreSignedOrderCredsConsumer, - viper.GetBool("enable-store-signed-order-creds-consumer")) - - // skus number of consumers to create for store signed order creds - ctx = context.WithValue(ctx, appctx.SkusNumberStoreSignedOrderCredsConsumer, - viper.GetInt("number-store-signed-order-creds-consumer")) - - // playstore json key - // json key is base64 - jsonKey, err := base64.StdEncoding.DecodeString(viper.GetString("playstore-json-key")) - if err != nil { - logger.Error().Err(err). - Msg("failed to decode the playstore json key") - } - ctx = context.WithValue(ctx, appctx.PlaystoreJSONKeyCTXKey, jsonKey) - - ctx = context.WithValue(ctx, appctx.AppleReceiptSharedKeyCTXKey, viper.GetString("apple-receipt-shared-key")) - - ctx, r, _, jobs := setupRouter(ctx, logger) - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - if enableJobWorkers { - for _, job := range jobs { - // iterate over jobs - for i := 0; i < job.Workers; i++ { - // spin up a job worker for each worker - logger.Debug().Msg("starting job worker") - go srv.JobWorker(ctx, job.Func, job.Cadence) - } - } - } - - go func() { - err := http.ListenAndServe(":9090", middleware.Metrics()) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("metrics HTTP server start failed!") - } - }() - - srv := http.Server{ - Addr: ":3333", - Handler: chi.ServerBaseContext(ctx, r), - ReadTimeout: 3 * time.Second, - WriteTimeout: 20 * time.Second, - } - err = srv.ListenAndServe() - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("HTTP server start failed!") - } - return nil -} - -func newSrvStatusFromCtx(ctx context.Context) map[string]any { - uh, _ := ctx.Value(appctx.DisableUpholdLinkingCTXKey).(bool) - g, _ := ctx.Value(appctx.DisableGeminiLinkingCTXKey).(bool) - bf, _ := ctx.Value(appctx.DisableBitflyerLinkingCTXKey).(bool) - zp, _ := ctx.Value(appctx.DisableZebPayLinkingCTXKey).(bool) - - result := map[string]interface{}{ - "wallet": map[string]bool{ - "uphold": !uh, - "gemini": !g, - "bitflyer": !bf, - "zebpay": !zp, - }, - } - - return result -} diff --git a/services/grant/cmd/grant_test.go b/services/grant/cmd/grant_test.go deleted file mode 100644 index 481840159..000000000 --- a/services/grant/cmd/grant_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package cmd - -import ( - "context" - "testing" - - should "github.com/stretchr/testify/assert" - - appctx "github.com/brave-intl/bat-go/libs/context" -) - -func TestNewSrvStatusFromCtx(t *testing.T) { - ctx := context.TODO() - - ctx = context.WithValue(ctx, appctx.DisableUpholdLinkingCTXKey, true) - ctx = context.WithValue(ctx, appctx.DisableGeminiLinkingCTXKey, true) - ctx = context.WithValue(ctx, appctx.DisableBitflyerLinkingCTXKey, true) - ctx = context.WithValue(ctx, appctx.DisableZebPayLinkingCTXKey, true) - - act := newSrvStatusFromCtx(ctx) - exp := map[string]interface{}{ - "wallet": map[string]bool{ - "uphold": false, - "gemini": false, - "bitflyer": false, - "zebpay": false, - }, - } - - should.Equal(t, exp, act) -} diff --git a/services/grant/datastore.go b/services/grant/datastore.go deleted file mode 100644 index 30557836e..000000000 --- a/services/grant/datastore.go +++ /dev/null @@ -1,137 +0,0 @@ -package grant - -import ( - "os" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/datastore" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/getsentry/sentry-go" - "github.com/rs/zerolog/log" - "github.com/shopspring/decimal" - - // needed magically? - - // needed for magic migration - _ "github.com/golang-migrate/migrate/v4/source/file" -) - -// Datastore abstracts over the underlying datastore -type Datastore interface { - datastore.Datastore - // GetGrantsOrderedByExpiry returns ordered grant claims with optional promotion type filter - GetGrantsOrderedByExpiry(wallet walletutils.Info, promotionType string) ([]Grant, error) -} - -// ReadOnlyDatastore includes all database methods that can be made with a read only db connection -type ReadOnlyDatastore interface { - datastore.Datastore - // GetGrantsOrderedByExpiry returns ordered grant claims with optional promotion type filter - GetGrantsOrderedByExpiry(wallet walletutils.Info, promotionType string) ([]Grant, error) -} - -// Postgres is a Datastore wrapper around a postgres database -type Postgres struct { - datastore.Postgres -} - -// NewDB creates a new Postgres Datastore -func NewDB(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (Datastore, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if pg != nil { - return &DatastoreWithPrometheus{ - base: &Postgres{*pg}, instanceName: "grant_datastore", - }, err - } - return nil, err -} - -// NewRODB creates a new Postgres RO Datastore -func NewRODB(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (ReadOnlyDatastore, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if pg != nil { - return &ReadOnlyDatastoreWithPrometheus{ - base: &Postgres{*pg}, instanceName: "grant_ro_datastore", - }, err - } - return nil, err -} - -// NewPostgres creates postgres connections -func NewPostgres() (Datastore, ReadOnlyDatastore, error) { - var grantRoPg ReadOnlyDatastore - grantPg, err := NewDB("", true, "grant", "grant_db") - if err != nil { - sentry.CaptureException(err) - log.Panic().Err(err).Msg("Must be able to init postgres connection to start") - } - - roDB := os.Getenv("RO_DATABASE_URL") - if len(roDB) > 0 { - grantRoPg, err = NewRODB(roDB, false, "grant", "grant_read_only_db") - if err != nil { - sentry.CaptureException(err) - log.Error().Err(err).Msg("Could not start reader postgres connection") - } - } - return grantPg, grantRoPg, err -} - -// GetGrantsOrderedByExpiry returns ordered grant claims for a wallet -func (pg *Postgres) GetGrantsOrderedByExpiry(wallet walletutils.Info, promotionType string) ([]Grant, error) { - type GrantResult struct { - Grant - ApproximateValue decimal.Decimal `db:"approximate_value"` - CreatedAt time.Time `db:"created_at"` - ExpiresAt time.Time `db:"expires_at"` - Platform string `db:"platform"` - } - - if len(promotionType) == 0 { - promotionType = "{ads,ugp}" - } - - statement := ` -select - claims.id, - claims.approximate_value, - claims.promotion_id, - promotions.created_at, - promotions.expires_at, - promotions.promotion_type, - promotions.platform -from claims inner join promotions -on claims.promotion_id = promotions.id -where - claims.wallet_id = $1 and - not claims.redeemed and - claims.legacy_claimed and - promotions.promotion_type = any($2::text[]) and - promotions.expires_at > now() -order by promotions.expires_at` - - var grantResults []GrantResult - - err := pg.RawDB().Select(&grantResults, statement, wallet.ID, promotionType) - if err != nil { - return []Grant{}, err - } - grants := make([]Grant, len(grantResults)) - - for i, grant := range grantResults { - { - tmp := altcurrency.BAT - grant.AltCurrency = &tmp - } - grant.Probi = grant.AltCurrency.ToProbi(grant.ApproximateValue) - grant.MaturityTimestamp = grant.CreatedAt.Unix() - grant.ExpiryTimestamp = grant.ExpiresAt.Unix() - if grant.Type == "ugp" && grant.Platform == "android" { - grant.Type = "android" - } - grants[i] = grant.Grant - } - - return grants, nil -} diff --git a/services/grant/grant.go b/services/grant/grant.go deleted file mode 100644 index e7856eb02..000000000 --- a/services/grant/grant.go +++ /dev/null @@ -1,32 +0,0 @@ -package grant - -import ( - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/wallet" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// Grant - a "check" good for the amount inscribed, redeemable between maturityTime and expiryTime -type Grant struct { - AltCurrency *altcurrency.AltCurrency `json:"altcurrency" valid:"-"` - GrantID uuid.UUID `json:"grantId" valid:"-" db:"id"` - Probi decimal.Decimal `json:"probi" valid:"-"` - PromotionID uuid.UUID `json:"promotionId" valid:"-" db:"promotion_id"` - MaturityTimestamp int64 `json:"maturityTime" valid:"-"` - ExpiryTimestamp int64 `json:"expiryTime" valid:"-"` - Type string `json:"type,omitempty" valid:"-" db:"promotion_type"` - ProviderID *uuid.UUID `json:"providerId,omitempty" valid:"-"` -} - -// ByExpiryTimestamp implements sort.Interface for []Grant based on the ExpiryTimestamp field. -type ByExpiryTimestamp []Grant - -func (a ByExpiryTimestamp) Len() int { return len(a) } -func (a ByExpiryTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByExpiryTimestamp) Less(i, j int) bool { return a[i].ExpiryTimestamp < a[j].ExpiryTimestamp } - -// GetGrantsOrderedByExpiry returns ordered grant claims for a wallet with optional promotionType filter -func (service *Service) GetGrantsOrderedByExpiry(wallet wallet.Info, promotionType string) ([]Grant, error) { - return service.ReadableDatastore().GetGrantsOrderedByExpiry(wallet, promotionType) -} diff --git a/services/grant/grant_test.go b/services/grant/grant_test.go deleted file mode 100644 index 92bd82f7f..000000000 --- a/services/grant/grant_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package grant - -import ( - "sort" - "testing" -) - -func TestByExpiryTimestamp(t *testing.T) { - grants := []Grant{{ExpiryTimestamp: 12345}, {ExpiryTimestamp: 1234}} - sort.Sort(ByExpiryTimestamp(grants)) - var last int64 - for _, grant := range grants { - if grant.ExpiryTimestamp < last { - t.Error("Order is not correct") - last = grant.ExpiryTimestamp - } - } -} diff --git a/services/grant/instrumented_datastore.go b/services/grant/instrumented_datastore.go deleted file mode 100755 index cdc569074..000000000 --- a/services/grant/instrumented_datastore.go +++ /dev/null @@ -1,132 +0,0 @@ -package grant - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/grant -i Datastore -t ../../.prom-gowrap.tmpl -o instrumented_datastore.go -l "" - -import ( - "time" - - walletutils "github.com/brave-intl/bat-go/libs/wallet" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// DatastoreWithPrometheus implements Datastore interface with all methods wrapped -// with Prometheus metrics -type DatastoreWithPrometheus struct { - base Datastore - instanceName string -} - -var datastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "grant_datastore_duration_seconds", - Help: "datastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewDatastoreWithPrometheus returns an instance of the Datastore decorated with prometheus summary metric -func NewDatastoreWithPrometheus(base Datastore, instanceName string) DatastoreWithPrometheus { - return DatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// BeginTx implements Datastore -func (_d DatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// GetGrantsOrderedByExpiry implements Datastore -func (_d DatastoreWithPrometheus) GetGrantsOrderedByExpiry(wallet walletutils.Info, promotionType string) (ga1 []Grant, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetGrantsOrderedByExpiry", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetGrantsOrderedByExpiry(wallet, promotionType) -} - -// Migrate implements Datastore -func (_d DatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements Datastore -func (_d DatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements Datastore -func (_d DatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements Datastore -func (_d DatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements Datastore -func (_d DatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} diff --git a/services/grant/instrumented_read_only_datastore.go b/services/grant/instrumented_read_only_datastore.go deleted file mode 100644 index f6020e490..000000000 --- a/services/grant/instrumented_read_only_datastore.go +++ /dev/null @@ -1,132 +0,0 @@ -package grant - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/grant -i ReadOnlyDatastore -t ../../.prom-gowrap.tmpl -o instrumented_read_only_datastore.go -l "" - -import ( - "time" - - walletutils "github.com/brave-intl/bat-go/libs/wallet" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -// ReadOnlyDatastoreWithPrometheus implements ReadOnlyDatastore interface with all methods wrapped -// with Prometheus metrics -type ReadOnlyDatastoreWithPrometheus struct { - base ReadOnlyDatastore - instanceName string -} - -var readonlydatastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "grant_readonly_datastore_duration_seconds", - Help: "readonlydatastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewReadOnlyDatastoreWithPrometheus returns an instance of the ReadOnlyDatastore decorated with prometheus summary metric -func NewReadOnlyDatastoreWithPrometheus(base ReadOnlyDatastore, instanceName string) ReadOnlyDatastoreWithPrometheus { - return ReadOnlyDatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// BeginTx implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// GetGrantsOrderedByExpiry implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetGrantsOrderedByExpiry(wallet walletutils.Info, promotionType string) (ga1 []Grant, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetGrantsOrderedByExpiry", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetGrantsOrderedByExpiry(wallet, promotionType) -} - -// Migrate implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} diff --git a/services/grant/mockdatastore.go b/services/grant/mockdatastore.go deleted file mode 100644 index 8d5da1efa..000000000 --- a/services/grant/mockdatastore.go +++ /dev/null @@ -1,266 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./grant/datastore.go - -// Package grant is a generated GoMock package. -package grant - -import ( - reflect "reflect" - - wallet "github.com/brave-intl/bat-go/libs/wallet" - v4 "github.com/golang-migrate/migrate/v4" - gomock "github.com/golang/mock/gomock" - sqlx "github.com/jmoiron/sqlx" -) - -// MockDatastore is a mock of Datastore interface. -type MockDatastore struct { - ctrl *gomock.Controller - recorder *MockDatastoreMockRecorder -} - -// MockDatastoreMockRecorder is the mock recorder for MockDatastore. -type MockDatastoreMockRecorder struct { - mock *MockDatastore -} - -// NewMockDatastore creates a new mock instance. -func NewMockDatastore(ctrl *gomock.Controller) *MockDatastore { - mock := &MockDatastore{ctrl: ctrl} - mock.recorder = &MockDatastoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDatastore) EXPECT() *MockDatastoreMockRecorder { - return m.recorder -} - -// BeginTx mocks base method. -func (m *MockDatastore) BeginTx() (*sqlx.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeginTx") - ret0, _ := ret[0].(*sqlx.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BeginTx indicates an expected call of BeginTx. -func (mr *MockDatastoreMockRecorder) BeginTx() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockDatastore)(nil).BeginTx)) -} - -// GetGrantsOrderedByExpiry mocks base method. -func (m *MockDatastore) GetGrantsOrderedByExpiry(wallet wallet.Info, promotionType string) ([]Grant, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetGrantsOrderedByExpiry", wallet, promotionType) - ret0, _ := ret[0].([]Grant) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetGrantsOrderedByExpiry indicates an expected call of GetGrantsOrderedByExpiry. -func (mr *MockDatastoreMockRecorder) GetGrantsOrderedByExpiry(wallet, promotionType interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGrantsOrderedByExpiry", reflect.TypeOf((*MockDatastore)(nil).GetGrantsOrderedByExpiry), wallet, promotionType) -} - -// Migrate mocks base method. -func (m *MockDatastore) Migrate(arg0 ...uint) error { - m.ctrl.T.Helper() - varargs := []interface{}{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Migrate", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Migrate indicates an expected call of Migrate. -func (mr *MockDatastoreMockRecorder) Migrate(arg0 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Migrate", reflect.TypeOf((*MockDatastore)(nil).Migrate), arg0...) -} - -// NewMigrate mocks base method. -func (m *MockDatastore) NewMigrate() (*v4.Migrate, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewMigrate") - ret0, _ := ret[0].(*v4.Migrate) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewMigrate indicates an expected call of NewMigrate. -func (mr *MockDatastoreMockRecorder) NewMigrate() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewMigrate", reflect.TypeOf((*MockDatastore)(nil).NewMigrate)) -} - -// RawDB mocks base method. -func (m *MockDatastore) RawDB() *sqlx.DB { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RawDB") - ret0, _ := ret[0].(*sqlx.DB) - return ret0 -} - -// RawDB indicates an expected call of RawDB. -func (mr *MockDatastoreMockRecorder) RawDB() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDB", reflect.TypeOf((*MockDatastore)(nil).RawDB)) -} - -// RollbackTx mocks base method. -func (m *MockDatastore) RollbackTx(tx *sqlx.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RollbackTx", tx) -} - -// RollbackTx indicates an expected call of RollbackTx. -func (mr *MockDatastoreMockRecorder) RollbackTx(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTx", reflect.TypeOf((*MockDatastore)(nil).RollbackTx), tx) -} - -// RollbackTxAndHandle mocks base method. -func (m *MockDatastore) RollbackTxAndHandle(tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RollbackTxAndHandle", tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// RollbackTxAndHandle indicates an expected call of RollbackTxAndHandle. -func (mr *MockDatastoreMockRecorder) RollbackTxAndHandle(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTxAndHandle", reflect.TypeOf((*MockDatastore)(nil).RollbackTxAndHandle), tx) -} - -// MockReadOnlyDatastore is a mock of ReadOnlyDatastore interface. -type MockReadOnlyDatastore struct { - ctrl *gomock.Controller - recorder *MockReadOnlyDatastoreMockRecorder -} - -// MockReadOnlyDatastoreMockRecorder is the mock recorder for MockReadOnlyDatastore. -type MockReadOnlyDatastoreMockRecorder struct { - mock *MockReadOnlyDatastore -} - -// NewMockReadOnlyDatastore creates a new mock instance. -func NewMockReadOnlyDatastore(ctrl *gomock.Controller) *MockReadOnlyDatastore { - mock := &MockReadOnlyDatastore{ctrl: ctrl} - mock.recorder = &MockReadOnlyDatastoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockReadOnlyDatastore) EXPECT() *MockReadOnlyDatastoreMockRecorder { - return m.recorder -} - -// BeginTx mocks base method. -func (m *MockReadOnlyDatastore) BeginTx() (*sqlx.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeginTx") - ret0, _ := ret[0].(*sqlx.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BeginTx indicates an expected call of BeginTx. -func (mr *MockReadOnlyDatastoreMockRecorder) BeginTx() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockReadOnlyDatastore)(nil).BeginTx)) -} - -// GetGrantsOrderedByExpiry mocks base method. -func (m *MockReadOnlyDatastore) GetGrantsOrderedByExpiry(wallet wallet.Info, promotionType string) ([]Grant, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetGrantsOrderedByExpiry", wallet, promotionType) - ret0, _ := ret[0].([]Grant) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetGrantsOrderedByExpiry indicates an expected call of GetGrantsOrderedByExpiry. -func (mr *MockReadOnlyDatastoreMockRecorder) GetGrantsOrderedByExpiry(wallet, promotionType interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGrantsOrderedByExpiry", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetGrantsOrderedByExpiry), wallet, promotionType) -} - -// Migrate mocks base method. -func (m *MockReadOnlyDatastore) Migrate(arg0 ...uint) error { - m.ctrl.T.Helper() - varargs := []interface{}{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Migrate", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Migrate indicates an expected call of Migrate. -func (mr *MockReadOnlyDatastoreMockRecorder) Migrate(arg0 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Migrate", reflect.TypeOf((*MockReadOnlyDatastore)(nil).Migrate), arg0...) -} - -// NewMigrate mocks base method. -func (m *MockReadOnlyDatastore) NewMigrate() (*v4.Migrate, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewMigrate") - ret0, _ := ret[0].(*v4.Migrate) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewMigrate indicates an expected call of NewMigrate. -func (mr *MockReadOnlyDatastoreMockRecorder) NewMigrate() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewMigrate", reflect.TypeOf((*MockReadOnlyDatastore)(nil).NewMigrate)) -} - -// RawDB mocks base method. -func (m *MockReadOnlyDatastore) RawDB() *sqlx.DB { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RawDB") - ret0, _ := ret[0].(*sqlx.DB) - return ret0 -} - -// RawDB indicates an expected call of RawDB. -func (mr *MockReadOnlyDatastoreMockRecorder) RawDB() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDB", reflect.TypeOf((*MockReadOnlyDatastore)(nil).RawDB)) -} - -// RollbackTx mocks base method. -func (m *MockReadOnlyDatastore) RollbackTx(tx *sqlx.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RollbackTx", tx) -} - -// RollbackTx indicates an expected call of RollbackTx. -func (mr *MockReadOnlyDatastoreMockRecorder) RollbackTx(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTx", reflect.TypeOf((*MockReadOnlyDatastore)(nil).RollbackTx), tx) -} - -// RollbackTxAndHandle mocks base method. -func (m *MockReadOnlyDatastore) RollbackTxAndHandle(tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RollbackTxAndHandle", tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// RollbackTxAndHandle indicates an expected call of RollbackTxAndHandle. -func (mr *MockReadOnlyDatastoreMockRecorder) RollbackTxAndHandle(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTxAndHandle", reflect.TypeOf((*MockReadOnlyDatastore)(nil).RollbackTxAndHandle), tx) -} diff --git a/services/grant/service.go b/services/grant/service.go deleted file mode 100644 index 6b01cb661..000000000 --- a/services/grant/service.go +++ /dev/null @@ -1,53 +0,0 @@ -package grant - -import ( - "context" - - srv "github.com/brave-intl/bat-go/libs/service" - "github.com/brave-intl/bat-go/services/promotion" - "github.com/brave-intl/bat-go/services/wallet" -) - -// Service contains datastore -type Service struct { - baseCtx context.Context - Datastore Datastore - RoDatastore ReadOnlyDatastore - wallet *wallet.Service - promotion *promotion.Service - jobs []srv.Job -} - -// Jobs - Implement srv.JobService interface -func (s *Service) Jobs() []srv.Job { - return s.jobs -} - -// InitService initializes the grant service -func InitService( - ctx context.Context, - datastore Datastore, - roDatastore ReadOnlyDatastore, - walletService *wallet.Service, - promotionService *promotion.Service, -) (*Service, error) { - gs := &Service{ - baseCtx: ctx, - Datastore: datastore, - RoDatastore: roDatastore, - wallet: walletService, - promotion: promotionService, - } - - // setup runnable jobs - gs.jobs = []srv.Job{} - return gs, nil -} - -// ReadableDatastore returns a read only datastore if available, otherwise a normal datastore -func (s *Service) ReadableDatastore() ReadOnlyDatastore { - if s.RoDatastore != nil { - return s.RoDatastore - } - return s.Datastore -} diff --git a/services/nitro/nitro.go b/services/nitro/nitro.go index 05e5f2296..d73fe42a2 100644 --- a/services/nitro/nitro.go +++ b/services/nitro/nitro.go @@ -9,12 +9,12 @@ import ( "strings" "time" - rootcmd "github.com/brave-intl/bat-go/cmd" + rootcmd "github.com/brave-intl/payments-service/cmd" appctx "github.com/brave-intl/bat-go/libs/context" "github.com/brave-intl/bat-go/libs/logging" "github.com/brave-intl/bat-go/libs/nitro" - srvcmd "github.com/brave-intl/bat-go/services/cmd" - "github.com/brave-intl/bat-go/services/payments" + srvcmd "github.com/brave-intl/payments-service/services/cmd" + "github.com/brave-intl/payments-service/services/payments" "github.com/go-chi/chi" "github.com/mdlayher/vsock" diff --git a/services/payments/cmd/payments.go b/services/payments/cmd/payments.go index 85eb7a23b..4370d055f 100644 --- a/services/payments/cmd/payments.go +++ b/services/payments/cmd/payments.go @@ -5,8 +5,8 @@ import ( // pprof imports _ "net/http/pprof" - cmdutils "github.com/brave-intl/bat-go/cmd" - srvcmd "github.com/brave-intl/bat-go/services/cmd" + cmdutils "github.com/brave-intl/payments-service/cmd" + srvcmd "github.com/brave-intl/payments-service/services/cmd" "github.com/spf13/cobra" "github.com/spf13/viper" ) diff --git a/services/payments/cmd/rest_run.go b/services/payments/cmd/rest_run.go index 8f1400e12..41d782136 100644 --- a/services/payments/cmd/rest_run.go +++ b/services/payments/cmd/rest_run.go @@ -7,11 +7,11 @@ import ( // pprof imports _ "net/http/pprof" - rootcmd "github.com/brave-intl/bat-go/cmd" + rootcmd "github.com/brave-intl/payments-service/cmd" appctx "github.com/brave-intl/bat-go/libs/context" "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/services/cmd" - "github.com/brave-intl/bat-go/services/payments" + "github.com/brave-intl/payments-service/services/cmd" + "github.com/brave-intl/payments-service/services/payments" sentry "github.com/getsentry/sentry-go" "github.com/go-chi/chi" "github.com/spf13/cobra" diff --git a/services/payments/cmd/worker.go b/services/payments/cmd/worker.go index 1f2dc659f..2320413f3 100644 --- a/services/payments/cmd/worker.go +++ b/services/payments/cmd/worker.go @@ -1,10 +1,10 @@ package cmd import ( - rootcmd "github.com/brave-intl/bat-go/cmd" + rootcmd "github.com/brave-intl/payments-service/cmd" appctx "github.com/brave-intl/bat-go/libs/context" "github.com/brave-intl/bat-go/libs/redisconsumer" - "github.com/brave-intl/bat-go/services/payments" + "github.com/brave-intl/payments-service/services/payments" "github.com/spf13/cobra" "github.com/spf13/viper" ) diff --git a/services/promotion/README.md b/services/promotion/README.md deleted file mode 100644 index 904e9519a..000000000 --- a/services/promotion/README.md +++ /dev/null @@ -1,33 +0,0 @@ -### Drain Info Endpoint Operation - -You are able to lookup the status of a wallet's drains by performing this API call -and including an environment specific simple secret access token. - -``` -curl -H"Authorization: Bearer " "http:///v1/promotions/custodian-drain-info/" -HTTP/1.1 200 OK -Connection: close -Content-Type: application/json - -{ - "status": "success", - "drains": [ - { - "batch_id": "ca72d2e2-14b2-44da-b8e2-e5fce7f87263", - "custodian": { - "provider": "bitflyer", - "deposit_destination": "f2b3cc8a-597d-4eeb-a2f9-23f65dbd2495" - }, - "promotions_drained": [ - { - "promotion_id": "daf95421-4388-4c7e-9ac3-4b476f8a5c79", - "state": "errored", - "errcode": "reputation-failed", - "value": "0.25" - } - ], - "value": "0.25" - } - ] -} -``` diff --git a/services/promotion/avro.go b/services/promotion/avro.go deleted file mode 100644 index 16cfbadce..000000000 --- a/services/promotion/avro.go +++ /dev/null @@ -1,53 +0,0 @@ -package promotion - -const suggestionEventSchema = `{ - "namespace": "brave.grants", - "type": "record", - "name": "suggestion", - "doc": "This message is sent when a client suggests to 'spend' a grant", - "fields": [ - { "name": "id", "type": "string" }, - { "name": "type", "type": "string" }, - { "name": "channel", "type": "string" }, - { "name": "createdAt", "type": "string" }, - { "name": "totalAmount", "type": "string" }, - { "name": "orderId", "type": "string", "default": "" }, - { "name": "funding", - "type": { - "type": "array", - "items": { - "type": "record", - "name": "funding", - "doc": "This record represents a funding source, currently a promotion.", - "fields": [ - { "name": "type", "type": "string" }, - { "name": "amount", "type": "string" }, - { "name": "cohort", "type": "string" }, - { "name": "promotion", "type": "string" } - ] - } - } - } - ]}` - -const adminAttestationEventSchema = `{ - "type": "record", - "name": "DefaultMessage", - "fields": [ - { "name": "wallet_id", "type": "string" }, - { "name": "service", "type": "string" }, - { "name": "signal", "type": "string" }, - { "name": "score", "type": "int" }, - { "name": "justification", "type": "string" }, - { "name": "created_at", "type": "string" } - ]}` - -// AdminAttestationEvent - kafka admin attestation event -type AdminAttestationEvent struct { - WalletID string `json:"wallet_id"` - Service string `json:"service"` - Signal string `json:"signal"` - Score int32 `json:"score"` - Justification string `json:"justification"` - CreatedAt string `json:"created_at"` -} diff --git a/services/promotion/claim.go b/services/promotion/claim.go deleted file mode 100644 index 655735efe..000000000 --- a/services/promotion/claim.go +++ /dev/null @@ -1,252 +0,0 @@ -package promotion - -import ( - "context" - "errors" - "net/http" - "strconv" - "time" - - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/jsonutils" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/getsentry/sentry-go" - "github.com/lib/pq" - "github.com/prometheus/client_golang/prometheus" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// Claim encapsulates a redeemed or unredeemed ("pre-registered") claim to a promotion by a wallet -type Claim struct { - ID uuid.UUID `db:"id"` - CreatedAt time.Time `db:"created_at"` - PromotionID uuid.UUID `db:"promotion_id"` - WalletID uuid.UUID `db:"wallet_id"` - ApproximateValue decimal.Decimal `db:"approximate_value"` - Redeemed bool `db:"redeemed"` - Bonus decimal.Decimal `db:"bonus"` - LegacyClaimed bool `db:"legacy_claimed"` - RedeemedAt pq.NullTime `db:"redeemed_at"` - Drained bool `db:"drained"` - DrainedAt pq.NullTime `db:"drained_at"` - UpdatedAt pq.NullTime `db:"updated_at"` - ClaimType *string `db:"claim_type"` -} - -// SuggestionsNeeded calculates the number of suggestion credentials needed to fulfill the value of this claim -func (claim *Claim) SuggestionsNeeded(promotion *Promotion) (int, error) { - if claim.PromotionID != promotion.ID { - return 0, errors.New("incorrect promotion passed") - } - amount := int(claim.ApproximateValue.Mul(decimal.NewFromFloat(float64(promotion.SuggestionsPerGrant)).Div(promotion.ApproximateValue)).Round(0).IntPart()) - if amount < 1 { - return 1, nil - } - return amount, nil -} - -// ClaimCreds encapsulates the credentials to be signed in response to a valid claim -type ClaimCreds struct { - ID uuid.UUID `db:"claim_id"` - IssuerID uuid.UUID `db:"issuer_id"` - BlindedCreds jsonutils.JSONStringArray `db:"blinded_creds"` - SignedCreds *jsonutils.JSONStringArray `db:"signed_creds"` - BatchProof *string `db:"batch_proof"` - PublicKey *string `db:"public_key"` - CreatedAt pq.NullTime `db:"created_at"` - UpdatedAt pq.NullTime `db:"updated_at"` -} - -func blindCredsEq(a, b []string) bool { - if len(a) != len(b) { - return false - } - // a and b must have same values in same order - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} - -var errClaimedDifferentBlindCreds = errors.New("blinded credentials do not match what was already claimed") - -// ClaimPromotionForWallet attempts to claim the promotion on behalf of a wallet and returning the ClaimID -// It kicks off asynchronous signing of the credentials on success -func (service *Service) ClaimPromotionForWallet( - ctx context.Context, - promotionID uuid.UUID, - walletID uuid.UUID, - blindedCreds []string, -) (*uuid.UUID, error) { - - logger := logging.Logger(ctx, "ClaimPromotionForWallet") - - promotion, err := service.Datastore.GetPromotion(promotionID) - if err != nil { - return nil, err - } - if promotion == nil { - return nil, errors.New("promotion did not exist") - } - - wallet, err := service.wallet.Datastore.GetWallet(ctx, walletID) - if err != nil || wallet == nil { - return nil, errorutils.Wrap(err, "error getting wallet") - } - - claim, err := service.Datastore.GetClaimByWalletAndPromotion(wallet, promotion) - if err != nil { - return nil, errorutils.Wrap(err, "error checking previous claims for wallet") - } - - // check if we need to override the auto expiry of the promotion - overrideAutoExpiry := false - if claim != nil { - overrideAutoExpiry = claim.LegacyClaimed - } - - // check if promotion is claimable - if !promotion.Claimable(overrideAutoExpiry) { - return nil, &handlers.AppError{ - Message: "promotion is no longer active", - Code: http.StatusGone, - } - } - - if claim != nil { - // get the claim credentials to check if these blinded creds were used before - claimCreds, err := service.Datastore.GetClaimCreds(claim.ID) - if err != nil { - return nil, errorutils.Wrap(err, "error checking claim credentials for claims") - } - - if claim.Redeemed { - if claimCreds == nil { - // there are no stored claim creds for this claim - logger.Error(). - Str("wallet_id", walletID.String()). - Str("claim_id", claim.ID.String()). - Msg("nil claim credentials for claim") - return nil, errors.New("nil claim credentials recorded") - } - - // If this wallet already claimed and it was redeemed (legacy or into claim creds), return the claim id - // and the claim blinded tokens are the same - if blindCredsEq([]string(claimCreds.BlindedCreds), blindedCreds) { - return &claim.ID, nil - } - return nil, errClaimedDifferentBlindCreds - } - } - - // check if promotion is disabled, need different behavior than Gone - if !promotion.Active { - return nil, &handlers.AppError{ - Message: "promotion is disabled", - Code: http.StatusBadRequest, - } - } - - // This is skipped for legacy migration path as they passed a reputation check when originally claiming - if claim == nil || !claim.LegacyClaimed { - walletIsReputable, err := service.reputationClient.IsWalletReputable(ctx, walletID, promotion.Platform) - if err != nil { - return nil, err - } - - if !walletIsReputable { - return nil, errors.New("insufficient wallet reputation for grant claim") - } - } - - cohort := "control" - issuer, err := service.GetOrCreateIssuer(ctx, promotionID, cohort) - if err != nil { - return nil, err - } - - if promotion.Type == "ads" { - claim, err := service.Datastore.GetPreClaim(promotionID, wallet.ID) - if err != nil { - return nil, err - } - - if claim == nil { - return nil, errors.New("you cannot claim this promotion") - } - - suggestionsNeeded, err := claim.SuggestionsNeeded(promotion) - if err != nil { - return nil, err - } - if len(blindedCreds) != suggestionsNeeded { - return nil, errors.New("wrong number of blinded tokens included") - } - } else { - if len(blindedCreds) != promotion.SuggestionsPerGrant { - return nil, errors.New("wrong number of blinded tokens included") - } - } - - claim, err = service.Datastore.ClaimForWallet(promotion, issuer, wallet, jsonutils.JSONStringArray(blindedCreds)) - if err != nil { - return nil, err - } - - value, _ := claim.ApproximateValue.Float64() - labels := prometheus.Labels{ - "platform": promotion.Platform, - "type": promotion.Type, - "legacy": strconv.FormatBool(claim.LegacyClaimed), - } - countGrantsClaimedTotal.With(labels).Inc() - countGrantsClaimedBatTotal.With(labels).Add(value) - - go func() { - defer middleware.ConcurrentGoRoutines.With( - prometheus.Labels{ - "method": "ClaimJob", - }).Dec() - - middleware.ConcurrentGoRoutines.With( - prometheus.Labels{ - "method": "ClaimJob", - }).Inc() - _, err := service.RunNextClaimJob(ctx) - if err != nil { - sentry.CaptureException(err) - } - }() - - return &claim.ID, nil -} - -// ClaimWorker attempts to work on a claim job by signing the blinded credentials of the client -type ClaimWorker interface { - SignClaimCreds(ctx context.Context, claimID uuid.UUID, issuer Issuer, blindedCreds []string) (*ClaimCreds, error) -} - -// SignClaimCreds signs the blinded credentials -func (service *Service) SignClaimCreds(ctx context.Context, claimID uuid.UUID, issuer Issuer, blindedCreds []string) (*ClaimCreds, error) { - resp, err := service.cbClient.SignCredentials(ctx, issuer.Name(), blindedCreds) - if err != nil { - return nil, err - } - - signedTokens := jsonutils.JSONStringArray(resp.SignedTokens) - - creds := &ClaimCreds{ - ID: claimID, - BlindedCreds: blindedCreds, - SignedCreds: &signedTokens, - BatchProof: &resp.BatchProof, - PublicKey: &issuer.PublicKey, - } - - return creds, nil -} diff --git a/services/promotion/claim_summary.go b/services/promotion/claim_summary.go deleted file mode 100644 index 0abba8c9c..000000000 --- a/services/promotion/claim_summary.go +++ /dev/null @@ -1,15 +0,0 @@ -package promotion - -import ( - "time" - - "github.com/shopspring/decimal" -) - -// ClaimSummary outlines the state of a wallet's claims -type ClaimSummary struct { - Amount decimal.Decimal `json:"amount" db:"amount"` - Earnings decimal.Decimal `json:"earnings" db:"earnings"` - LastClaim time.Time `json:"lastClaim" db:"last_claim"` - Type string `json:"type" db:"type"` -} diff --git a/services/promotion/claim_test.go b/services/promotion/claim_test.go deleted file mode 100644 index 1fb75f635..000000000 --- a/services/promotion/claim_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package promotion - -import ( - "fmt" - "testing" - - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/assert" -) - -var suggestionsNeededTests = []struct { - ApproximateValue float64 - SuggestionsNeeded int -}{ - {0.1, 1}, - {5.0, 20}, - {5.1, 20}, - {5.124, 20}, - {5.125, 21}, - {5.24, 21}, - {5.25, 21}, -} - -func TestSuggestionsNeeded(t *testing.T) { - var claim Claim - var promotion Promotion - - promotion.ID = uuid.NewV4() - claim.PromotionID = promotion.ID - - promotion.SuggestionsPerGrant = 40 - promotion.ApproximateValue = decimal.NewFromFloat(10.0) - - for _, tt := range suggestionsNeededTests { - t.Run(fmt.Sprintf("%f", tt.ApproximateValue), func(t *testing.T) { - claim.ApproximateValue = decimal.NewFromFloat(tt.ApproximateValue) - - suggestionsNeeded, err := claim.SuggestionsNeeded(&promotion) - assert.NoError(t, err) - - assert.Equal(t, tt.SuggestionsNeeded, suggestionsNeeded) - }) - } -} - -func TestBlindCredsEq(t *testing.T) { - var ( - a = []string{"a", "b", "c"} - a1 = []string{"a", "b", "c"} - b = []string{"b", "a", "c"} - c = []string{"d", "b", "c", "a"} - ) - if blindCredsEq(a, b) { - t.Error("two creds must retain the same ordering..") - } - if blindCredsEq(a, c) { - t.Error("two creds should have not been equal..") - } - if !blindCredsEq(a, a1) { - t.Error("two creds are equal should not be false") - } -} - -func TestClaimPromotion(t *testing.T) { - // t.Fatal("not implemented") -} diff --git a/services/promotion/controllers.go b/services/promotion/controllers.go deleted file mode 100644 index cf0405e99..000000000 --- a/services/promotion/controllers.go +++ /dev/null @@ -1,692 +0,0 @@ -package promotion - -import ( - "context" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "net/http" - "os" - "strconv" - "time" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/clients" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/jsonutils" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/brave-intl/bat-go/libs/responses" - "github.com/brave-intl/bat-go/libs/useragent" - "github.com/brave-intl/bat-go/libs/validators" - "github.com/go-chi/chi" - "github.com/prometheus/client_golang/prometheus" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// RouterV2 for promotion endpoints -func RouterV2(service *Service, vbatExpires time.Time) chi.Router { - r := chi.NewRouter() - r.Use(middleware.NewUpgradeRequiredByMiddleware(vbatExpires)) - if os.Getenv("ENV") != "local" { - r.Method("POST", "/", middleware.SimpleTokenAuthorizedOnly(CreatePromotion(service))) - } else { - r.Method("POST", "/", CreatePromotion(service)) - } - - // version 2 clobbered claims - r.Method("POST", "/reportclobberedclaims", middleware.InstrumentHandler("ReportClobberedClaims", PostReportClobberedClaims(service, 2))) - - return r -} - -// Router for promotion endpoints -func Router(service *Service, vbatExpires time.Time) chi.Router { - r := chi.NewRouter() - r.Use(middleware.NewUpgradeRequiredByMiddleware(vbatExpires)) - if os.Getenv("ENV") != "local" { - r.Method("POST", "/", middleware.SimpleTokenAuthorizedOnly(CreatePromotion(service))) - } else { - r.Method("POST", "/", CreatePromotion(service)) - } - - r.Method("GET", "/{claimType}/grants/summary", middleware.InstrumentHandler("GetClaimSummary", GetClaimSummary(service))) - r.Method("GET", "/", middleware.InstrumentHandler("GetAvailablePromotions", GetAvailablePromotions(service))) - // version 1 clobbered claims - r.Method("POST", "/reportclobberedclaims", middleware.InstrumentHandler("ReportClobberedClaims", PostReportClobberedClaims(service, 1))) - r.Method("POST", "/{promotionId}", middleware.HTTPSignedOnly(service)(middleware.InstrumentHandler("ClaimPromotion", ClaimPromotion(service)))) - r.Method("GET", "/{promotionId}/claims/{claimId}", middleware.InstrumentHandler("GetClaim", GetClaim(service))) - r.Method("POST", "/report-bap", middleware.HTTPSignedOnly(service)(middleware.InstrumentHandler("PostReportBAPEvent", PostReportBAPEvent(service)))) - return r -} - -// SuggestionsV2Router for suggestions endpoints -func SuggestionsV2Router(service *Service, vbatExpires time.Time) (chi.Router, error) { - r := chi.NewRouter() - r.Use(middleware.NewUpgradeRequiredByMiddleware(vbatExpires)) - var ( - enableLinkingDraining bool - err error - ) - // make sure that we only enable the DrainJob if we have linking/draining enabled - if os.Getenv("ENABLE_LINKING_DRAINING") != "" { - enableLinkingDraining, err = strconv.ParseBool(os.Getenv("ENABLE_LINKING_DRAINING")) - if err != nil { - return nil, fmt.Errorf("invalid enable_linking_draining flag: %w", err) - } - } - - if enableLinkingDraining { - r.Method("POST", "/claim", middleware.HTTPSignedOnly(service)(middleware.InstrumentHandler("DrainSuggestionV2", DrainSuggestionV2(service)))) - } - return r, nil -} - -// SuggestionsRouter for suggestions endpoints -func SuggestionsRouter(service *Service, vbatExpires time.Time) (chi.Router, error) { - r := chi.NewRouter() - r.Use(middleware.NewUpgradeRequiredByMiddleware(vbatExpires)) - r.Method("POST", "/", middleware.InstrumentHandler("MakeSuggestion", MakeSuggestion(service))) - - var ( - enableLinkingDraining bool - err error - ) - // make sure that we only enable the DrainJob if we have linking/draining enabled - if os.Getenv("ENABLE_LINKING_DRAINING") != "" { - enableLinkingDraining, err = strconv.ParseBool(os.Getenv("ENABLE_LINKING_DRAINING")) - if err != nil { - return nil, fmt.Errorf("invalid enable_linking_draining flag: %w", err) - } - } - - if enableLinkingDraining { - r.Method("POST", "/claim", middleware.HTTPSignedOnly(service)(middleware.InstrumentHandler("DrainSuggestion", DrainSuggestion(service)))) - } - return r, nil -} - -// WalletEventRouter for reporting bat loss events -func WalletEventRouter(service *Service, vbatExpires time.Time) chi.Router { - r := chi.NewRouter() - r.Use(middleware.NewUpgradeRequiredByMiddleware(vbatExpires)) - r.Method("POST", "/{walletId}/events/batloss/{reportId}", middleware.HTTPSignedOnly(service)(middleware.InstrumentHandler("PostReportWalletEvent", PostReportWalletEvent(service)))) - return r -} - -// LookupVerifier based on the HTTP signing keyID, which in our case is the walletID -func (service *Service) LookupVerifier(ctx context.Context, keyID string) (context.Context, *httpsignature.Verifier, error) { - walletID, err := uuid.FromString(keyID) - if err != nil { - return nil, nil, errorutils.Wrap(err, "KeyID format is invalid") - } - - wallet, err := service.wallet.GetWallet(ctx, walletID) - if err != nil { - return nil, nil, errorutils.Wrap(err, "error getting wallet") - } - - if wallet == nil { - return nil, nil, nil - } - - var publicKey httpsignature.Ed25519PubKey - if len(wallet.PublicKey) > 0 { - var err error - publicKey, err = hex.DecodeString(wallet.PublicKey) - if err != nil { - return nil, nil, err - } - } - tmp := httpsignature.Verifier(publicKey) - return ctx, &tmp, nil -} - -// PromotionsResponse is a list of known promotions to be consumed by the browser -type PromotionsResponse struct { - Promotions []Promotion `json:"promotions"` -} - -// GetAvailablePromotions is the handler for getting available promotions -func GetAvailablePromotions(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - filter string - walletID = new(inputs.ID) - ) - walletIDText := r.URL.Query().Get("paymentId") - - if len(walletIDText) > 0 { - if err := inputs.DecodeAndValidateString(context.Background(), walletID, walletIDText); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "paymentId": err.Error(), - }, - ) - } - - logging.AddWalletIDToContext(r.Context(), *walletID.UUID()) - filter = "walletID" - } - - platform := r.URL.Query().Get("platform") - if len(platform) > 0 && !validators.IsPlatform(platform) { - return handlers.ValidationError("request query parameter", map[string]string{ - "platform": fmt.Sprintf("platform '%s' is not supported", platform), - }) - } - - migrate := false - migrateParam := r.URL.Query().Get("migrate") - if migrateParam == "true" { - migrate = true - } - - promotions, err := service.GetAvailablePromotions(r.Context(), walletID.UUID(), platform, migrate) - if err != nil { - return handlers.WrapError(err, "Error getting available promotions", http.StatusInternalServerError) - } - if promotions == nil { - return handlers.WrapError(err, "Error finding wallet", http.StatusNotFound) - } - - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(&PromotionsResponse{*promotions}); err != nil { - panic(err) - } - if len(filter) == 0 { - filter = "none" - } - promotionGetCount.With(prometheus.Labels{ - "filter": filter, - "migrate": fmt.Sprint(migrate), - }).Inc() - for _, promotion := range *promotions { - promotionExposureCount.With(prometheus.Labels{ - "id": promotion.ID.String(), - }).Inc() - } - return nil - }) -} - -// ClaimRequest includes the ID of the wallet attempting to claim and blinded credentials which to be signed -type ClaimRequest struct { - WalletID uuid.UUID `json:"paymentId" valid:"-"` - BlindedCreds []string `json:"blindedCreds" valid:"base64"` -} - -// ClaimResponse includes a ClaimID which can later be used to check the status of the claim -type ClaimResponse struct { - ClaimID uuid.UUID `json:"claimId"` -} - -// ClaimPromotion is the handler for claiming a particular promotion by a wallet -func ClaimPromotion(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req ClaimRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - logging.AddWalletIDToContext(r.Context(), req.WalletID) - - keyID, err := middleware.GetKeyID(r.Context()) - if err != nil { - return handlers.WrapError(err, "Error looking up http signature info", http.StatusBadRequest) - } - if req.WalletID.String() != keyID { - return handlers.ValidationError("Error validating request", map[string]string{ - "paymentId": "paymentId must match signature", - }) - } - - var promotionID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), promotionID, chi.URLParam(r, "promotionId")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "promotionId": err.Error(), - }, - ) - } - - claimID, err := service.ClaimPromotionForWallet(r.Context(), *promotionID.UUID(), req.WalletID, req.BlindedCreds) - - if err != nil { - var ( - target *errorutils.ErrorBundle - status = http.StatusBadRequest - ) - - if errors.Is(err, errClaimedDifferentBlindCreds) { - status = http.StatusConflict - } - - if errors.As(err, &target) { - err = target - response, ok := target.Data().(clients.HTTPState) - if ok { - if response.Status != 0 { - status = response.Status - } - err = fmt.Errorf(target.Error()) - } - } - return handlers.WrapError(err, "Error claiming promotion", status) - } - - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(&ClaimResponse{*claimID}); err != nil { - panic(err) - } - return nil - }) -} - -// GetClaimResponse includes signed credentials and a batch proof showing they were signed by the public key -type GetClaimResponse struct { - SignedCreds jsonutils.JSONStringArray `json:"signedCreds"` - BatchProof string `json:"batchProof"` - PublicKey string `json:"publicKey"` -} - -// GetClaim is the handler for checking on a particular claim's status -func GetClaim(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var claimID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), claimID, chi.URLParam(r, "claimId")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "claimId": err.Error(), - }, - ) - } - - claim, err := service.Datastore.GetClaimCreds(*claimID.UUID()) - if err != nil { - return handlers.WrapError(err, "Error getting claim", http.StatusBadRequest) - } - - if claim == nil { - return &handlers.AppError{ - Message: "Claim does not exist", - Code: http.StatusNotFound, - Data: map[string]interface{}{}, - } - } - - if claim.SignedCreds == nil { - return &handlers.AppError{ - Message: "Claim has been accepted but is not ready", - Code: http.StatusAccepted, - Data: map[string]interface{}{}, - } - } - - resp := &GetClaimResponse{ - SignedCreds: *claim.SignedCreds, - BatchProof: *claim.BatchProof, - PublicKey: *claim.PublicKey, - } - - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(resp); err != nil { - panic(err) - } - return nil - - }) -} - -// GetClaimSummary returns an summary of grants claimed by a given wallet -func GetClaimSummary(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - claimType := chi.URLParam(r, "claimType") - walletIDQuery := r.URL.Query().Get("paymentID") - if len(walletIDQuery) == 0 { - walletIDQuery = r.URL.Query().Get("paymentId") - } - walletID, err := uuid.FromString(walletIDQuery) - - if err != nil { - return handlers.ValidationError("query parameter", map[string]string{ - "paymentId": "must be a uuidv4", - }) - } - - logging.AddWalletIDToContext(r.Context(), walletID) - - wallet, err := service.wallet.ReadableDatastore().GetWallet(r.Context(), walletID) - if err != nil { - return handlers.WrapError(err, "Error finding wallet", http.StatusInternalServerError) - } - - if wallet == nil { - err := fmt.Errorf("wallet not found id: '%s'", walletID.String()) - return handlers.WrapError(err, "Error finding wallet", http.StatusNotFound) - } - - summary, err := service.ReadableDatastore().GetClaimSummary(walletID, claimType) - if err != nil { - return handlers.WrapError(err, "Error aggregating wallet claims", http.StatusInternalServerError) - } - - if summary == nil { - w.WriteHeader(http.StatusNoContent) - return nil - } - - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(summary); err != nil { - panic(err) - } - return nil - }) -} - -// SuggestionRequest includes a suggestion payload and credentials to be redeemed -type SuggestionRequest struct { - Suggestion string `json:"suggestion" valid:"base64"` - Credentials []CredentialBinding `json:"credentials"` -} - -// MakeSuggestion is the handler for making a suggestion using credentials -func MakeSuggestion(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req SuggestionRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - err = service.Suggest(r.Context(), req.Credentials, req.Suggestion) - if err != nil { - switch err.(type) { - case govalidator.Error: - return handlers.WrapValidationError(err) - case govalidator.Errors: - return handlers.WrapValidationError(err) - default: - // FIXME - return handlers.WrapError(err, "Error making suggestion", http.StatusBadRequest) - } - } - - w.WriteHeader(http.StatusOK) - return nil - }) -} - -var errGone = errors.New("endpoint is gone") - -// DrainSuggestionV2 is the handler for draining ad suggestions for a verified wallet -func DrainSuggestionV2(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return handlers.WrapError(errGone, "gone", http.StatusGone) - }) -} - -// DrainSuggestion is the handler for draining ad suggestions for a verified wallet -func DrainSuggestion(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return handlers.WrapError(errGone, "gone", http.StatusGone) - }) -} - -// CreatePromotionRequest includes information needed to create a promotion -type CreatePromotionRequest struct { - Type string `json:"type" valid:"in(ads|ugp)"` - NumGrants int `json:"numGrants" valid:"required"` - Value decimal.Decimal `json:"value" valid:"required"` - Platform string `json:"platform" valid:"platform,optional"` - Active bool `json:"active" valid:"-"` -} - -// CreatePromotionResponse includes information about the created promotion -type CreatePromotionResponse struct { - Promotion -} - -// CreatePromotion is the handler for creating a promotion -func CreatePromotion(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req CreatePromotionRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - promotion, err := service.Datastore.CreatePromotion(req.Type, req.NumGrants, req.Value, req.Platform) - if err != nil { - return handlers.WrapError(err, "Error creating promotion", http.StatusBadRequest) - } - - if req.Active { - err = service.Datastore.ActivatePromotion(promotion) - if err != nil { - return handlers.WrapError(err, "Error marking promotion active", http.StatusBadRequest) - } - } - - _, err = service.CreateIssuer(r.Context(), promotion.ID, "control") - if err != nil { - return handlers.WrapError(err, "Error making control issuer", http.StatusInternalServerError) - } - - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(&CreatePromotionResponse{Promotion: *promotion}); err != nil { - panic(err) - } - return nil - }) -} - -// ClobberedClaimsRequest holds the data needed to report claims that were clobbered by client bug -type ClobberedClaimsRequest struct { - ClaimIDs []uuid.UUID `json:"claimIds" valid:"required"` -} - -// Validate - implement validatable -func (ccr *ClobberedClaimsRequest) Validate(ctx context.Context) error { - // govalidator "required" does not always work on arrays, just make sure there - // are more than 0 items - if ccr.ClaimIDs == nil || len(ccr.ClaimIDs) < 1 { - return errors.New("request should have more than zero items") - } - return nil -} - -// PostReportClobberedClaims is the handler for reporting claims that were clobbered by client bug -func PostReportClobberedClaims(service *Service, version int) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req ClobberedClaimsRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - err = req.Validate(r.Context()) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - if len(req.ClaimIDs) == 0 { - return handlers.WrapValidationError(errors.New("ClaimIDs: required, cannot be empty")) - } - - // govalidator does not always catch empty array on required - if len(req.ClaimIDs) == 0 { - return handlers.WrapValidationError(errors.New("ClaimIDs: required, cannot be empty")) - } - - err = service.Datastore.InsertClobberedClaims(r.Context(), req.ClaimIDs, version) - if err != nil { - return handlers.WrapError(err, "Error making control issuer", http.StatusInternalServerError) - } - - w.WriteHeader(http.StatusOK) - return nil - }) -} - -// BatLossPayload holds the data needed to report that bat has been lost by client bug -type BatLossPayload struct { - Amount decimal.Decimal `json:"amount" valid:"required"` -} - -// PostReportWalletEvent is the handler for reporting bat was lost by client bug -func PostReportWalletEvent(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req BatLossPayload - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - walletID, err := uuid.FromString(chi.URLParam(r, "walletId")) - if err != nil { - return handlers.ValidationError("query parameter", map[string]string{ - "paymentId": "must be a uuidv4", - }) - } - reportIDParam := chi.URLParam(r, "reportId") - reportID, err := strconv.Atoi(reportIDParam) - if err != nil { - return handlers.ValidationError("report id is not an int", map[string]string{ - "reportId": "report id (" + reportIDParam + ") must be an integer", - }) - } - platform := useragent.ParsePlatform(r.UserAgent()) - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - created, err := service.Datastore.InsertBATLossEvent( - r.Context(), - walletID, - reportID, - req.Amount, - platform, - ) - if err != nil { - if errors.Is(err, errorutils.ErrConflictBATLossEvent) { - return handlers.WrapError(err, "Error inserting bat loss event", http.StatusConflict) - } - return handlers.WrapError(err, "Error inserting bat loss event", http.StatusInternalServerError) - } - status := http.StatusOK - if created { - status = http.StatusCreated - } - return handlers.RenderContent(r.Context(), nil, w, status) - }) -} - -// BapReportPayload holds the data needed to report that bat has been lost by client bug -type BapReportPayload struct { - Amount decimal.Decimal `json:"amount" valid:"required"` -} - -// BapReportResp holds the data needed to report that bat has been lost by client bug -type BapReportResp struct { - ReportBapID *uuid.UUID `json:"reportBapId" valid:"required"` -} - -// PostReportBAPEvent is the handler for reporting bat was lost by client bug -func PostReportBAPEvent(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req BapReportPayload - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - // get wallet id from http signature id - id, err := middleware.GetKeyID(r.Context()) - if err != nil { - return handlers.ValidationError("no id in http signature", map[string]string{ - "id": "missing", - }) - } - - walletID, err := uuid.FromString(id) - if err != nil { - return handlers.ValidationError("query parameter", map[string]string{ - "paymentId": "must be a uuidv4", - }) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - // do the magic here - bapReportID, err := service.Datastore.InsertBAPReportEvent( - r.Context(), - walletID, - req.Amount, - ) - - if err != nil { - if errors.Is(err, errorutils.ErrConflictBAPReportEvent) { - return handlers.WrapError(err, "Error inserting bap report, paymentId already reported", http.StatusConflict) - } - return handlers.WrapError(err, "Error inserting bap report", http.StatusInternalServerError) - } - return handlers.RenderContent(r.Context(), BapReportResp{ReportBapID: bapReportID}, w, http.StatusOK) - }) -} - -// CustodianDrainInfoResponse - the response to a custodian drain info request -type CustodianDrainInfoResponse struct { - responses.Meta - Drains []CustodianDrain `json:"drains,omitempty"` -} - -// GetCustodianDrainInfo is the handler which provides information about a particular paymentId's drains -func GetCustodianDrainInfo(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return handlers.WrapError(errGone, "gone", http.StatusGone) - }) -} - -// DrainJobRequest holds data for drain job requests -type DrainJobRequest struct { - Erred bool `json:"erred"` -} - -// PatchDrainJobErred is the handler for toggling a drain job as retriable -func PatchDrainJobErred(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - w.WriteHeader(http.StatusGone) - w.Write([]byte{}) - return nil - } -} diff --git a/services/promotion/controllers_test.go b/services/promotion/controllers_test.go deleted file mode 100644 index e1be88717..000000000 --- a/services/promotion/controllers_test.go +++ /dev/null @@ -1,1288 +0,0 @@ -//go:build integration - -package promotion - -import ( - "bytes" - "context" - "crypto" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "os" - "strconv" - "strings" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients/cbr" - mockcb "github.com/brave-intl/bat-go/libs/clients/cbr/mock" - mockreputation "github.com/brave-intl/bat-go/libs/clients/reputation/mock" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/jsonutils" - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - "github.com/brave-intl/bat-go/libs/middleware" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/services/wallet" - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/rs/zerolog/log" - uuid "github.com/satori/go.uuid" - "github.com/segmentio/kafka-go" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type ControllersTestSuite struct { - suite.Suite -} - -func TestControllersTestSuite(t *testing.T) { - suite.Run(t, new(ControllersTestSuite)) -} - -func (suite *ControllersTestSuite) SetupSuite() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - m, err := pg.NewMigrate() - suite.Require().NoError(err, "Failed to create migrate instance") - - ver, dirty, _ := m.Version() - if dirty { - suite.Require().NoError(m.Force(int(ver))) - } - if ver > 0 { - suite.Require().NoError(m.Down(), "Failed to migrate down cleanly") - } - - suite.Require().NoError(pg.Migrate(), "Failed to fully migrate") - - enableSuggestionJob = true -} - -func (suite *ControllersTestSuite) SetupTest() { - suite.CleanDB() -} - -func (suite *ControllersTestSuite) TearDownTest() { - suite.CleanDB() -} - -func (suite *ControllersTestSuite) CleanDB() { - tables := []string{"claim_drain", "claim_creds", "claims", "wallets", "issuers", "promotions"} - - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - for _, table := range tables { - _, err = pg.RawDB().Exec("delete from " + table) - suite.Require().NoError(err, "Failed to get clean table") - } -} - -func (suite *ControllersTestSuite) TestGetPromotions() { - - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - cbClient, err := cbr.New() - suite.Require().NoError(err, "Failed to create challenge bypass client") - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - walletID := uuid.NewV4() - w := walletutils.Info{ - ID: walletID.String(), - Provider: "uphold", - ProviderID: "-", - AltCurrency: nil, - PublicKey: "-", - LastBalance: nil, - } - - err = walletDB.InsertWallet(context.Background(), &w) - suite.Require().NoError(err, "Failed to insert wallet") - - service := &Service{ - Datastore: pg, - cbClient: cbClient, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - } - handler := GetAvailablePromotions(service) - - urlWithPlatform := func(platform string) string { - return fmt.Sprintf("/promotions?paymentId=%s&platform=%s", walletID.String(), platform) - } - - promotionJSON := func(available bool, promotion *Promotion) string { - return `{ - "approximateValue": "` + promotion.ApproximateValue.String() + `", - "available": ` + strconv.FormatBool(available) + `, - "createdAt": "` + promotion.CreatedAt.Format(time.RFC3339Nano) + `", - "claimableUntil": "` + promotion.ClaimableUntil.Format(time.RFC3339Nano) + `", - "expiresAt": "` + promotion.ExpiresAt.Format(time.RFC3339Nano) + `", - "id": "` + promotion.ID.String() + `", - "legacyClaimed": ` + strconv.FormatBool(promotion.LegacyClaimed) + `, - "platform": "` + promotion.Platform + `", - "publicKeys" : ["1"], - "suggestionsPerGrant": ` + strconv.Itoa(promotion.SuggestionsPerGrant) + `, - "type": "ugp", - "version": 5 - }` - } - - reqFailure, err := http.NewRequest("GET", urlWithPlatform("noexist"), nil) - suite.Require().NoError(err, "Failed to create get promotions request") - - reqOSX, err := http.NewRequest("GET", urlWithPlatform("osx"), nil) - suite.Require().NoError(err, "Failed to create get promotions request") - - reqAndroid, err := http.NewRequest("GET", urlWithPlatform("android"), nil) - suite.Require().NoError(err, "Failed to create get promotions request") - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, reqFailure) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - expectationFailure := `{ - "code":400, - "message": "Error validating request query parameter", - "data": { - "validationErrors": { - "platform": "platform 'noexist' is not supported" - } - } - }` - suite.Assert().JSONEq(expectationFailure, rr.Body.String(), "unexpected result") - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqOSX) - - suite.Require().Equal(http.StatusOK, rr.Code) - suite.Assert().JSONEq(`{"promotions": []}`, rr.Body.String(), "unexpected result") - - promotionGeneric, err := service.Datastore.CreatePromotion("ugp", 2, decimal.NewFromFloat(15.0), "") - suite.Require().NoError(err, "Failed to create a general promotion") - - // do a get promotion to get the promotion with the claimable until - promotionGeneric, err = service.Datastore.GetPromotion(promotionGeneric.ID) - suite.Require().NoError(err, "Failed to get the general promotion") - - promotionDesktop, err := service.Datastore.CreatePromotion("ugp", 2, decimal.NewFromFloat(20.0), "desktop") - suite.Require().NoError(err, "Failed to create osx promotion") - - // do a get promotion to get the promotion with the claimable until - promotionDesktop, err = service.Datastore.GetPromotion(promotionDesktop.ID) - suite.Require().NoError(err, "Failed to get the desktop promotion") - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqOSX) - suite.Require().Equal(http.StatusOK, rr.Code) - expectedOSX := `{ - "promotions": [ - ] - }` - suite.Require().JSONEq(expectedOSX, rr.Body.String(), "unexpected result") - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqAndroid) - suite.Require().Equal(http.StatusOK, rr.Code) - expectedAndroid := `{ - "promotions": [ - ] - }` - suite.Assert().JSONEq(expectedAndroid, rr.Body.String(), "unexpected result") - - err = service.Datastore.ActivatePromotion(promotionGeneric) - suite.Require().NoError(err, "Failed to activate promotion") - // promotion needs an issuer - _, err = service.Datastore.InsertIssuer(&Issuer{ - ID: uuid.NewV4(), - PromotionID: promotionGeneric.ID, - Cohort: "control", - PublicKey: `1`, - }) - suite.Require().NoError(err, "Failed to insert issuer promotion") - - err = service.Datastore.ActivatePromotion(promotionDesktop) - suite.Require().NoError(err, "Failed to activate promotion") - // promotion needs an issuer - _, err = service.Datastore.InsertIssuer(&Issuer{ - ID: uuid.NewV4(), - PromotionID: promotionDesktop.ID, - Cohort: "control", - PublicKey: `1`, - }) - suite.Require().NoError(err, "Failed to insert issuer promotion") - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqOSX) - suite.Require().Equal(http.StatusOK, rr.Code) - expectedOSX = `{ - "promotions": [ - ` + promotionJSON(true, promotionGeneric) + `, - ` + promotionJSON(true, promotionDesktop) + ` - ] - }` - - suite.Assert().JSONEq(expectedOSX, rr.Body.String(), "unexpected result") - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqAndroid) - suite.Require().Equal(http.StatusOK, rr.Code) - expectedAndroid = `{ - "promotions": [ - ` + promotionJSON(true, promotionGeneric) + ` - ] - }` - suite.Assert().JSONEq(expectedAndroid, rr.Body.String(), "unexpected result") - - statement := ` - insert into claims (promotion_id, wallet_id, approximate_value, legacy_claimed) - values ($1, $2, $3, true)` - _, err = pg.RawDB().Exec(statement, promotionDesktop.ID, w.ID, promotionDesktop.ApproximateValue) - promotionDesktop.LegacyClaimed = true - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqOSX) - suite.Require().Equal(http.StatusOK, rr.Code) - expectedOSX = `{ - "promotions": [ - ` + promotionJSON(true, promotionGeneric) + ` - ] - }` - suite.Assert().JSONEq(expectedOSX, rr.Body.String(), "unexpected result") - - url := fmt.Sprintf("/promotions?paymentId=%s&platform=osx&migrate=true", walletID.String()) - reqOSX, err = http.NewRequest("GET", url, nil) - suite.Require().NoError(err, "Failed to create get promotions request") - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, reqOSX) - suite.Require().Equal(http.StatusOK, rr.Code) - expectedOSX = `{ - "promotions": [ - ` + promotionJSON(true, promotionGeneric) + `, - ` + promotionJSON(true, promotionDesktop) + ` - ] - }` - suite.Assert().JSONEq(expectedOSX, rr.Body.String(), "unexpected result") -} - -// ClaimPromotion helper that calls promotion endpoint and does assertions -func (suite *ControllersTestSuite) ClaimPromotion(service *Service, w walletutils.Info, privKey crypto.Signer, - promotion *Promotion, blindedCreds []string, claimStatus int) *uuid.UUID { - - handler := middleware.HTTPSignedOnly(service)(ClaimPromotion(service)) - - walletID, err := uuid.FromString(w.ID) - suite.Require().NoError(err) - - claimReq := ClaimRequest{ - WalletID: walletID, - BlindedCreds: blindedCreds, - } - - body, err := json.Marshal(&claimReq) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/promotion/{promotionId}", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = w.ID - s.Headers = []string{"digest", "(request-target)"} - - err = s.Sign(privKey, crypto.Hash(0), req) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("promotionId", promotion.ID.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - if claimStatus != 200 { - // return early if claim is supposed to fail - suite.Require().Equal(rr.Code, claimStatus, string(rr.Body.Bytes())) - return nil - } - // if claim was not supposed to fail, or rr.Code is supposed to be ok following line fails - suite.Require().Equal(http.StatusOK, rr.Code) - - var claimResp ClaimResponse - err = json.Unmarshal(rr.Body.Bytes(), &claimResp) - suite.Require().NoError(err) - return &claimResp.ClaimID -} - -func (suite *ControllersTestSuite) WaitForClaimToPropagate(service *Service, promotion *Promotion, claimID *uuid.UUID) { - handler := GetClaim(service) - - req, err := http.NewRequest("GET", "/promotion/{promotionId}/claims/{claimId}", nil) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("promotionId", promotion.ID.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - ctx, _ := context.WithTimeout(req.Context(), 500*time.Millisecond) - cID := *claimID - rctx.URLParams.Add("claimId", cID.String()) - req = req.WithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - for rr.Code != http.StatusOK { - if rr.Code == http.StatusBadRequest { - break - } - select { - case <-ctx.Done(): - break - default: - time.Sleep(50 * time.Millisecond) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - } - } - suite.Require().Equal(http.StatusOK, rr.Code, "Async signing timed out") - - var getClaimResp GetClaimResponse - err = json.Unmarshal(rr.Body.Bytes(), &getClaimResp) - suite.Require().NoError(err) - suite.Require().Equal(promotion.SuggestionsPerGrant, len(getClaimResp.SignedCreds), "Signed credentials should have the same length") -} - -func (suite *ControllersTestSuite) TestClaimGrant() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - cbClient, err := cbr.New() - suite.Require().NoError(err, "Failed to create challenge bypass client") - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err, "Failed to create wallet keypair") - - walletID := uuid.NewV4() - bat := altcurrency.BAT - info := walletutils.Info{ - ID: walletID.String(), - Provider: "uphold", - ProviderID: "-", - AltCurrency: &bat, - PublicKey: hex.EncodeToString(publicKey), - LastBalance: nil, - } - - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockReputation.EXPECT().IsWalletReputable( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return( - true, - nil, - ) - - service := &Service{ - Datastore: pg, - cbClient: cbClient, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - reputationClient: mockReputation, - } - - promotion, err := service.Datastore.CreatePromotion("ugp", 2, decimal.NewFromFloat(15.0), "") - suite.Require().NoError(err, "Failed to create promotion") - err = service.Datastore.ActivatePromotion(promotion) - suite.Require().NoError(err, "Failed to activate promotion") - - blindedCreds := make([]string, promotion.SuggestionsPerGrant) - for i := range blindedCreds { - blindedCreds[i] = "yoGo7zfMr5vAzwyyFKwoFEsUcyUlXKY75VvWLfYi7go=" - } - - err = walletDB.UpsertWallet(context.Background(), &info) - suite.Require().NoError(err, "Failed to insert wallet") - - claimID := suite.ClaimPromotion(service, info, privKey, promotion, blindedCreds, http.StatusOK) - suite.WaitForClaimToPropagate(service, promotion, claimID) - - handler := GetAvailablePromotions(service) - req, err := http.NewRequest("GET", fmt.Sprintf("/promotions?paymentId=%s&platform=osx", walletID.String()), nil) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - expected := `{ - "promotions": [] - }` - suite.Assert().JSONEq(expected, rr.Body.String(), "Expected public key to appear in promotions endpoint") - - mockReputation.EXPECT().IsWalletReputable( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return( - true, - nil, - ) - - promotion, _, claim := suite.setupAdsClaim(service, &info, 0) - - handler2 := middleware.HTTPSignedOnly(service)(ClaimPromotion(service)) - - // blindedCreds should be the wrong length - claimReq := ClaimRequest{ - WalletID: walletID, - BlindedCreds: blindedCreds, - } - - body, err := json.Marshal(&claimReq) - suite.Require().NoError(err) - - req, err = http.NewRequest("POST", "/promotion/{promotionId}", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = info.ID - s.Headers = []string{"digest", "(request-target)"} - - err = s.Sign(privKey, crypto.Hash(0), req) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("promotionId", promotion.ID.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr = httptest.NewRecorder() - handler2.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Assert().JSONEq(`{"message":"Error claiming promotion: wrong number of blinded tokens included","code":400}`, rr.Body.String()) - - mockReputation.EXPECT().IsWalletReputable( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return( - true, - nil, - ) - - blindedCreds = make([]string, int(claim.ApproximateValue.Mul(decimal.NewFromFloat(float64(promotion.SuggestionsPerGrant)).Div(promotion.ApproximateValue)).IntPart())) - for i := range blindedCreds { - blindedCreds[i] = "yoGo7zfMr5vAzwyyFKwoFEsUcyUlXKY75VvWLfYi7go=" - } - - claimReq.BlindedCreds = blindedCreds - - body, err = json.Marshal(&claimReq) - suite.Require().NoError(err) - - req, err = http.NewRequest("POST", "/promotion/{promotionId}", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - err = s.Sign(privKey, crypto.Hash(0), req) - suite.Require().NoError(err) - - rctx = chi.NewRouteContext() - rctx.URLParams.Add("promotionId", promotion.ID.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr = httptest.NewRecorder() - handler2.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) -} - -func (suite *ControllersTestSuite) TestGetClaimSummary() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - service := &Service{ - Datastore: pg, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - } - - missingWalletID := uuid.NewV4().String() - body, code := suite.checkGetClaimSummary(service, missingWalletID, "ads") - suite.Require().Equal(http.StatusNotFound, code, "a 404 is sent back") - suite.Assert().JSONEq(`{ - "code": 404, - "message": "Error finding wallet: wallet not found id: '`+missingWalletID+`'" - }`, body, "an error is returned") - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - blindedCreds := jsonutils.JSONStringArray([]string{publicKey}) - walletID := uuid.NewV4().String() - info := &walletutils.Info{ - ID: walletID, - Provider: "uphold", - ProviderID: uuid.NewV4().String(), - PublicKey: publicKey, - } - err = service.wallet.Datastore.UpsertWallet(context.Background(), info) - suite.Require().NoError(err, "the wallet failed to be inserted") - - // no content returns an empty string on protocol level - body, code = suite.checkGetClaimSummary(service, walletID, "ads") - suite.Assert().Equal(``, body) - suite.Require().Equal(http.StatusNoContent, code) - - body, code = suite.checkGetClaimSummary(service, "", "ads") - suite.Assert().JSONEq(`{ - "message": "Error validating query parameter", - "code": 400, - "data": { - "validationErrors": { - "paymentId": "must be a uuidv4" - } - } - }`, body, "body should return a payment id validation error") - suite.Require().Equal(http.StatusBadRequest, code) - - // not ignored promotion - promotion, issuer, claim := suite.setupAdsClaim(service, info, 0) - - _, err = pg.ClaimForWallet(promotion, issuer, info, blindedCreds) - suite.Require().NoError(err, "apply claim to wallet") - - body, code = suite.checkGetClaimSummary(service, walletID, "ads") - suite.Require().Equal(http.StatusOK, code) - suite.Assert().JSONEq(`{ - "amount": "30", - "earnings": "30", - "lastClaim": "`+claim.CreatedAt.Format(time.RFC3339Nano)+`", - "type": "ads" - }`, body, "expected a aggregated claim response") - - // ignored promotion (brave transfer - priorClaim := claim - promotion, issuer, claim = suite.setupAdsClaim(service, info, 0) - // set this promotion as a transfer promotion id, and some other random uuid to have more than one - os.Setenv("BRAVE_TRANSFER_PROMOTION_IDS", - fmt.Sprintf("%s %s", promotion.ID.String(), "d41ba588-ab18-4300-a180-d2dc01a22371")) - - _, err = pg.ClaimForWallet(promotion, issuer, info, blindedCreds) - suite.Require().NoError(err, "apply claim to wallet") - - body, code = suite.checkGetClaimSummary(service, walletID, "ads") - suite.Require().Equal(http.StatusOK, code) - // assert you get existing values - suite.Assert().JSONEq(`{ - "amount": "30", - "earnings": "30", - "lastClaim": "`+priorClaim.CreatedAt.Format(time.RFC3339Nano)+`", - "type": "ads" - }`, body, "expected a aggregated claim response") - - // not ignored bonus promotion - promotion, issuer, claim = suite.setupAdsClaim(service, info, 20) - - _, err = pg.ClaimForWallet(promotion, issuer, info, blindedCreds) - suite.Require().NoError(err, "apply claim to wallet") - - body, code = suite.checkGetClaimSummary(service, walletID, "ads") - suite.Require().Equal(http.StatusOK, code) - suite.Assert().JSONEq(`{ - "amount": "40", - "earnings": "40", - "lastClaim": "`+claim.CreatedAt.Format(time.RFC3339Nano)+`", - "type": "ads" - }`, body, "expected a aggregated claim response") -} - -func (suite *ControllersTestSuite) setupAdsClaim(service *Service, w *walletutils.Info, claimBonus float64) (*Promotion, *Issuer, *Claim) { - // promo amount can be different than individual grant amount - promoAmount := decimal.NewFromFloat(25.0) - promotion, err := service.Datastore.CreatePromotion("ads", 2, promoAmount, "") - suite.Require().NoError(err, "a promotion could not be created") - - publicKey := "dHuiBIasUO0khhXsWgygqpVasZhtQraDSZxzJW2FKQ4=" - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = service.Datastore.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - err = service.Datastore.ActivatePromotion(promotion) - suite.Require().NoError(err, "a promotion should be activated") - - grantAmount := decimal.NewFromFloat(30.0) - claim, err := service.Datastore.CreateClaim(promotion.ID, w.ID, grantAmount, decimal.NewFromFloat(claimBonus), false) - suite.Require().NoError(err, "create a claim for a promotion") - - return promotion, issuer, claim -} - -func (suite *ControllersTestSuite) checkGetClaimSummary(service *Service, walletID string, claimType string) (string, int) { - handler := GetClaimSummary(service) - req, err := http.NewRequest("GET", "/promotion/{claimType}/grants/total?paymentId="+walletID, nil) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("claimType", claimType) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - return rr.Body.String(), rr.Code -} - -func (suite *ControllersTestSuite) TestCreatePromotion() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - Datastore: pg, - cbClient: mockCB, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - } - var issuerName string - mockCB.EXPECT(). - CreateIssuer(gomock.Any(), gomock.Any(), gomock.Eq(defaultMaxTokensPerIssuer)). - DoAndReturn(func(ctx context.Context, name string, maxTokens int) error { - issuerName = name - return nil - }) - mockCB.EXPECT(). - GetIssuer(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, name string) (*cbr.IssuerResponse, error) { - return &cbr.IssuerResponse{ - Name: issuerName, - PublicKey: "", - }, nil - }) - handler := CreatePromotion(service) - - createRequest := CreatePromotionRequest{ - Type: "ugp", - NumGrants: 10, - Value: decimal.NewFromFloat(20.0), - Platform: "desktop", - Active: true, - } - - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code, fmt.Sprintf("failure body: %s", rr.Body.String())) -} - -func (suite *ControllersTestSuite) TestReportClobberedClaims() { - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - pg, _, err := NewPostgres() - suite.Require().NoError(err, "could not connect to db") - - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - Datastore: pg, - reputationClient: mockReputation, - cbClient: mockCB, - } - - handler := PostReportClobberedClaims(service, 1) - - claimIDs := []uuid.UUID{uuid.NewV4(), uuid.NewV4(), uuid.NewV4()} - requestPayloadStruct := ClobberedClaimsRequest{ - ClaimIDs: claimIDs, - } - payload, err := json.Marshal(&requestPayloadStruct) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/promotions/reportclaimsummary", bytes.NewBuffer(payload)) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - suite.Require().Equal(http.StatusOK, rr.Code) - - var clobberedCreds []ClobberedCreds - err = pg.RawDB().Select(&clobberedCreds, `select * from clobbered_claims;`) - suite.Require().NoError(err) - - var clobberedCredsIDs []uuid.UUID - for _, clobberedCred := range clobberedCreds { - clobberedCredsIDs = append(clobberedCredsIDs, clobberedCred.ID) - } - suite.Assert().ElementsMatch(claimIDs, clobberedCredsIDs) -} - -func (suite *ControllersTestSuite) TestClobberedClaims_Empty() { - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - reputationClient: mockReputation, - cbClient: mockCB, - } - - handler := PostReportClobberedClaims(service, 1) - requestPayloadStruct := ClobberedClaimsRequest{ - ClaimIDs: []uuid.UUID{}, - } - payload, err := json.Marshal(&requestPayloadStruct) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/promotions/reportclaimsummary", bytes.NewBuffer(payload)) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - suite.Require().Equal(http.StatusBadRequest, rr.Code) -} - -func (suite *ControllersTestSuite) TestPostReportWalletEvent() { - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - pg, _, err := NewPostgres() - suite.Require().NoError(err, "could not connect to db") - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - Datastore: pg, - reputationClient: mockReputation, - cbClient: mockCB, - } - handler := PostReportWalletEvent(service) - walletID1 := uuid.NewV4() - walletID2 := uuid.NewV4() - - run := func(walletID uuid.UUID, amount decimal.Decimal, ua string) *httptest.ResponseRecorder { - requestPayload := BATLossEvent{ - Amount: amount, - } - payload, err := json.Marshal(&requestPayload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v1/wallets/"+walletID.String()+"/events/batloss/1", bytes.NewBuffer([]byte(payload))) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("walletId", walletID.String()) - rctx.URLParams.Add("reportId", "1") - req.Header.Add("User-Agent", ua) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - return rr - } - suite.Require().Equal(http.StatusCreated, run(walletID1, decimal.NewFromFloat(10), "").Code) - suite.Require().Equal(http.StatusOK, run(walletID1, decimal.NewFromFloat(10), "").Code) - suite.Require().Equal(http.StatusConflict, run(walletID1, decimal.NewFromFloat(11), "").Code) - - walletEvents := []BATLossEvent{} - suite.Require().NoError(pg.RawDB().Select(&walletEvents, `select * from bat_loss_events`)) - serializedActual1, err := json.Marshal(&walletEvents) - serializedExpected1, err := json.Marshal([]BATLossEvent{{ - ID: walletEvents[0].ID, - WalletID: walletID1, - ReportID: 1, - Amount: decimal.NewFromFloat(10), - Platform: "", - }}) - suite.Require().JSONEq(string(serializedExpected1), string(serializedActual1)) - - wallet2Loss := decimal.NewFromFloat(29.4902814) - macUA := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" - suite.Require().Equal(http.StatusCreated, run(walletID2, decimal.NewFromFloat(29.4902814), macUA).Code) - - walletEvents = []BATLossEvent{} - suite.Require().NoError(pg.RawDB().Select(&walletEvents, `select * from bat_loss_events;`)) - serializedActual2, err := json.Marshal(&walletEvents) - serializedExpected2, err := json.Marshal([]BATLossEvent{{ - ID: walletEvents[0].ID, - WalletID: walletID1, - ReportID: 1, - Amount: decimal.NewFromFloat(10), - Platform: "", - }, { - ID: walletEvents[1].ID, - WalletID: walletID2, - ReportID: 1, - Amount: wallet2Loss, - Platform: "osx", - }}) - suite.Require().JSONEq(string(serializedExpected2), string(serializedActual2)) -} - -func (suite *ControllersTestSuite) TestClaimCompatibility() { - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - pg, _, err := NewPostgres() - suite.Require().NoError(err, "could not connect to db") - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "could not connect to db") - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - Datastore: pg, - reputationClient: mockReputation, - cbClient: mockCB, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - } - - now := time.Now().UTC() - threeMonthsAgo := now.AddDate(0, -3, 0) - later := now.Add(1000 * time.Second) - scenarios := []struct { - Legacy bool // set the claim as legacy - Type string // the type of promotion (ugp/ads) - PromoActive bool // set the promotion to be active - CreatedAt time.Time // set the created at time - ExpiresAt time.Time // set the expiration time - ClaimStatus int // the claim request status - ChecksReputation bool // reputation will be checked - }{ - { - Legacy: false, - Type: "ugp", - PromoActive: true, - CreatedAt: now, - ExpiresAt: later, - ClaimStatus: http.StatusOK, - ChecksReputation: true, - }, - { - Legacy: false, - Type: "ugp", - PromoActive: false, - CreatedAt: now, - ExpiresAt: later, - ClaimStatus: http.StatusBadRequest, - ChecksReputation: true, - }, - { - Legacy: true, - Type: "ugp", - PromoActive: true, - CreatedAt: now, - ExpiresAt: later, - ClaimStatus: http.StatusOK, - ChecksReputation: false, - }, - { - Legacy: true, - Type: "ugp", - PromoActive: false, - CreatedAt: now, - ExpiresAt: later, - ClaimStatus: http.StatusBadRequest, - // these are irrelevant if claim is gone - ChecksReputation: false, - }, - { - Legacy: true, - Type: "ugp", - PromoActive: false, - CreatedAt: now, - ExpiresAt: now, - ClaimStatus: http.StatusGone, - // these are irrelevant if claim is gone - ChecksReputation: false, - }, - { - Legacy: true, - Type: "ugp", - PromoActive: true, - CreatedAt: now, - ExpiresAt: now, - ClaimStatus: http.StatusGone, - // these are irrelevant if claim is gone - ChecksReputation: false, - }, - { - Legacy: true, // if legacy is true this status should result in OK - Type: "ugp", - PromoActive: true, - CreatedAt: threeMonthsAgo, - ExpiresAt: later, - // should be okay, because if the claim is legacy we do not auto expire - ClaimStatus: http.StatusOK, - // these are irrelevant if claim is gone - ChecksReputation: false, - }, - } - for _, test := range scenarios { - suite.CleanDB() - walletID := uuid.NewV4() - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err, "Failed to create wallet keypair") - bat := altcurrency.BAT - hexPublicKey := hex.EncodeToString(publicKey) - info := walletutils.Info{ - ID: walletID.String(), - Provider: "uphold", - ProviderID: "-", - AltCurrency: &bat, - PublicKey: hexPublicKey, - LastBalance: nil, - } - suite.Require().NoError(service.wallet.Datastore.UpsertWallet(context.Background(), &info), "could not insert wallet") - - blindedCreds := []string{"hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY="} - signedCreds := []string{"hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY="} - batchProof := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - promotionValue := decimal.NewFromFloat(0.25) - promotion, err := pg.CreatePromotion("ugp", 1, promotionValue, "") - suite.Require().NoError(err, "Create promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: hexPublicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - _, err = pg.RawDB().Exec( - "update promotions set expires_at = $2, created_at = $3 where id = $1", - promotion.ID, - test.ExpiresAt, - test.CreatedAt, - ) - suite.Require().NoError(err, "setting the expires_at property shouldn't fail") - if !test.PromoActive { - suite.Require().NoError(pg.DeactivatePromotion(promotion), "deactivating a promotion should succeed") - } - - if test.Legacy { - _, err = service.Datastore.CreateClaim(promotion.ID, info.ID, promotionValue, decimal.NewFromFloat(0.0), test.Legacy) - suite.Require().NoError(err, "an error occurred when creating a claim for wallet") - } - - if test.PromoActive { - if test.ClaimStatus == http.StatusOK { - mockCB.EXPECT().SignCredentials(gomock.Any(), gomock.Any(), gomock.Eq(blindedCreds)).Return(&cbr.CredentialsIssueResponse{ - BatchProof: batchProof, - SignedTokens: signedCreds, - }, nil) - } - - // non legacy pathway - if test.ChecksReputation { - mockReputation.EXPECT(). - IsWalletReputable( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ). - Return( - true, - nil, - ) - } - } - - claimID := suite.ClaimPromotion( - service, - info, - privKey, - promotion, - blindedCreds, - test.ClaimStatus, - ) - if test.ClaimStatus == http.StatusOK && test.PromoActive { - suite.WaitForClaimToPropagate(service, promotion, claimID) - } - } -} - -// THIS CODE IS A QUICK AND DIRTY HACK -// WE SHOULD DELETE ALL OF THIS AND MOVE OVER TO THE PAYMENT SERVICE ONCE DEMO IS DONE. - -// CreateOrder creates orders given the total price, merchant ID, status and items of the order -func (suite *ControllersTestSuite) CreateOrder() (string, error) { - pg, _, err := NewPostgres() - tx := pg.RawDB().MustBegin() - defer pg.RollbackTx(tx) - - var id string - - err = tx.Get(&id, ` - INSERT INTO orders (total_price, merchant_id, status, currency) - VALUES ($1, $2, $3, $4) - RETURNING id - `, 0.25, "brave.com", "pending", "BAT") - - err = tx.Commit() - if err != nil { - return "", err - } - - return id, nil -} - -func (suite *ControllersTestSuite) TestBraveFundsTransaction() { - // Set a random suggestion topic each so the test suite doesn't fail when re-ran - SetSuggestionTopic(uuid.NewV4().String() + ".grant.suggestion") - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres ") - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - // FIXME stick kafka setup in suite setup - kafkaBrokers := os.Getenv("KAFKA_BROKERS") - - dialer, _, err := kafkautils.TLSDialer() - suite.Require().NoError(err) - conn, err := dialer.DialLeader(context.Background(), "tcp", strings.Split(kafkaBrokers, ",")[0], "suggestion", 0) - suite.Require().NoError(err) - - err = conn.CreateTopics(kafka.TopicConfig{Topic: suggestionTopic, NumPartitions: 1, ReplicationFactor: 1}) - suite.Require().NoError(err) - - offset, err := conn.ReadLastOffset() - suite.Require().NoError(err) - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err, "Failed to create wallet keypair") - - walletID := uuid.NewV4() - bat := altcurrency.BAT - info := walletutils.Info{ - ID: walletID.String(), - Provider: "uphold", - ProviderID: "-", - AltCurrency: &bat, - PublicKey: hex.EncodeToString(publicKey), - LastBalance: nil, - } - - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockReputation.EXPECT().IsWalletReputable( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return( - true, - nil, - ) - err = walletDB.InsertWallet(context.Background(), &info) - suite.Require().NoError(err, "Failed to insert wallet") - - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - Datastore: pg, - cbClient: mockCB, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - reputationClient: mockReputation, - } - - err = service.InitKafka(context.Background()) - suite.Require().NoError(err, "Failed to initialize kafka") - - promotion, err := service.Datastore.CreatePromotion("ugp", 2, decimal.NewFromFloat(0.25), "") - suite.Require().NoError(err, "Failed to create promotion") - err = service.Datastore.ActivatePromotion(promotion) - suite.Require().NoError(err, "Failed to activate promotion") - - issuerName := promotion.ID.String() + ":control" - issuerPublicKey := "dHuiBIasUO0khhXsWgygqpVasZhtQraDSZxzJW2FKQ4=" - blindedCreds := []string{"XhBPMjh4vMw+yoNjE7C5OtoTz2rCtfuOXO/Vk7UwWzY="} - signedCreds := []string{"NJnOyyL6YAKMYo6kSAuvtG+/04zK1VNaD9KdKwuzAjU="} - proof := "IiKqfk10e7SJ54Ud/8FnCf+sLYQzS4WiVtYAM5+RVgApY6B9x4CVbMEngkDifEBRD6szEqnNlc3KA8wokGV5Cw==" - sig := "PsavkSWaqsTzZjmoDBmSu6YxQ7NZVrs2G8DQ+LkW5xOejRF6whTiuUJhr9dJ1KlA+79MDbFeex38X5KlnLzvJw==" - preimage := "125KIuuwtHGEl35cb5q1OLSVepoDTgxfsvwTc7chSYUM2Zr80COP19EuMpRQFju1YISHlnB04XJzZYN2ieT9Ng==" - - mockCB.EXPECT().CreateIssuer(gomock.Any(), gomock.Eq(issuerName), gomock.Eq(defaultMaxTokensPerIssuer)).Return(nil) - mockCB.EXPECT().GetIssuer(gomock.Any(), gomock.Eq(issuerName)).Return(&cbr.IssuerResponse{ - Name: issuerName, - PublicKey: issuerPublicKey, - }, nil) - mockCB.EXPECT().SignCredentials(gomock.Any(), gomock.Eq(issuerName), gomock.Eq(blindedCreds)).Return(&cbr.CredentialsIssueResponse{ - BatchProof: proof, - SignedTokens: signedCreds, - }, nil) - - err = walletDB.UpsertWallet(context.Background(), &info) - suite.Require().NoError(err, "Failed to insert wallet") - - claimID := suite.ClaimPromotion(service, info, privKey, promotion, blindedCreds, http.StatusOK) - suite.WaitForClaimToPropagate(service, promotion, claimID) - - handler := MakeSuggestion(service) - - orderID, err := suite.CreateOrder() - suite.Require().NoError(err) - validOrderID := uuid.Must(uuid.FromString(orderID)) - - orderPending, err := service.Datastore.GetOrder(validOrderID) - suite.Require().NoError(err) - suite.Require().Equal("pending", orderPending.Status) - - suggestion := Suggestion{ - Type: "payment", - OrderID: &validOrderID, - Channel: "brave.com", - } - - suggestionBytes, err := json.Marshal(&suggestion) - suite.Require().NoError(err) - suggestionPayload := base64.StdEncoding.EncodeToString(suggestionBytes) - - suggestionReq := SuggestionRequest{ - Suggestion: suggestionPayload, - Credentials: []CredentialBinding{{ - PublicKey: issuerPublicKey, - Signature: sig, - TokenPreimage: preimage, - }}, - } - - mockCB.EXPECT().RedeemCredentials(gomock.Any(), gomock.Eq([]cbr.CredentialRedemption{{ - Issuer: issuerName, - TokenPreimage: preimage, - Signature: sig, - }}), gomock.Eq(suggestionPayload)).Return(nil) - - body, err := json.Marshal(&suggestionReq) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/suggestion", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - r := kafka.NewReader(kafka.ReaderConfig{ - Brokers: strings.Split(kafkaBrokers, ","), - Topic: suggestionTopic, - Dialer: service.kafkaDialer, - MaxWait: time.Second, - RebalanceTimeout: time.Second, - Logger: kafka.LoggerFunc(log.Printf), - }) - - // :cry: - err = r.SetOffset(offset) - suite.Require().NoError(err) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - - codec := service.codecs["suggestion"] - - suggestionEventBinary, err := r.ReadMessage(context.Background()) - suite.Require().NoError(err) - - suggestionEvent, _, err := codec.NativeFromBinary(suggestionEventBinary.Value) - suite.Require().NoError(err) - suite.Require().NotNil(suggestionEvent) - - for { - updatedOrder, err := service.Datastore.GetOrder(validOrderID) - suite.Require().NoError(err) - if updatedOrder.Status == "paid" { - break - } - <-time.After(10 * time.Millisecond) - } -} - -func (suite *ControllersTestSuite) TestPostReportBAPEvent() { - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - pg, _, err := NewPostgres() - suite.Require().NoError(err, "could not connect to db") - mockReputation := mockreputation.NewMockClient(mockCtrl) - mockCB := mockcb.NewMockClient(mockCtrl) - - service := &Service{ - Datastore: pg, - reputationClient: mockReputation, - cbClient: mockCB, - } - handler := PostReportBAPEvent(service) - walletID1 := uuid.NewV4() - - run := func(walletID uuid.UUID, amount decimal.Decimal) *httptest.ResponseRecorder { - requestPayload := BapReportPayload{ - Amount: amount, - } - payload, err := json.Marshal(&requestPayload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v1/promotions/report-bap", bytes.NewBuffer([]byte(payload))) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - ctx := middleware.AddKeyID(req.Context(), walletID1.String()) - req = req.WithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - return rr - } - suite.Require().Equal(http.StatusOK, run(walletID1, decimal.NewFromFloat(10)).Code) - suite.Require().Equal(http.StatusConflict, run(walletID1, decimal.NewFromFloat(10)).Code) - - BAPEvents := []BAPReport{} - suite.Require().NoError(pg.RawDB().Select(&BAPEvents, `select * from bap_report`)) - serializedActual1, err := json.Marshal(&BAPEvents) - serializedExpected1, err := json.Marshal([]BAPReport{{ - ID: BAPEvents[0].ID, - WalletID: walletID1, - Amount: decimal.NewFromFloat(10), - CreatedAt: BAPEvents[0].CreatedAt, - }}) - suite.Require().JSONEq(string(serializedExpected1), string(serializedActual1)) - -} diff --git a/services/promotion/custodian.go b/services/promotion/custodian.go deleted file mode 100644 index 8834528dd..000000000 --- a/services/promotion/custodian.go +++ /dev/null @@ -1,20 +0,0 @@ -package promotion - -import ( - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// Custodian - generic custodian output data -type Custodian struct { - Provider string `json:"provider,omitempty" db:"user_deposit_account_provider"` - DepositDestination string `json:"deposit_destination,omitempty" db:"user_deposit_destination"` -} - -// CustodianDrain - representation of a drain job -type CustodianDrain struct { - BatchID uuid.UUID `json:"batch_id"` - Custodian Custodian `json:"custodian,omitempty"` - PromotionsDrained []DrainInfo `json:"promotions_drained,omitempty"` - Value decimal.Decimal `json:"value"` -} diff --git a/services/promotion/datastore.go b/services/promotion/datastore.go deleted file mode 100644 index cd669b877..000000000 --- a/services/promotion/datastore.go +++ /dev/null @@ -1,1175 +0,0 @@ -package promotion - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "os" - "strings" - "time" - - "github.com/prometheus/client_golang/prometheus" - - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/cbr" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/jsonutils" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/getsentry/sentry-go" - "github.com/lib/pq" - "github.com/rs/zerolog/log" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -var desktopPlatforms = [...]string{"linux", "osx", "windows"} - -var ( - // metric for claim drains status - // custodians are gemini, bitflyer, uphold and unknown - // status are complete and failed - countClaimDrainStatus = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "count_claim_drain_status", - Help: "provides a count of the complete and failed claim drains partitioned by custodian and status", - }, - []string{"custodian", "status"}, - ) - // counter for flagged unusual - countDrainFlaggedUnusual = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "count_drain_flagged_unusual", - Help: "provides a count of unusual drain flagged results", - ConstLabels: prometheus.Labels{"service": "promotions"}, - }) -) - -func init() { - prometheus.MustRegister(countClaimDrainStatus) - prometheus.MustRegister(withdrawalLimitHit) - prometheus.MustRegister(countDrainFlaggedUnusual) -} - -// ClobberedCreds holds data of claims that have been clobbered and when they were first reported -type ClobberedCreds struct { - ID uuid.UUID `db:"id"` - CreatedAt time.Time `db:"created_at"` - Version int `db:"version"` -} - -// BATLossEvent holds info about wallet events -type BATLossEvent struct { - ID uuid.UUID `db:"id" json:"id"` - WalletID uuid.UUID `db:"wallet_id" json:"walletId"` - ReportID int `db:"report_id" json:"reportId"` - Amount decimal.Decimal `db:"amount" json:"amount"` - Platform string `db:"platform" json:"platform"` -} - -// Datastore abstracts over the underlying datastore -type Datastore interface { - datastore.Datastore - // ActivatePromotion marks a particular promotion as active - ActivatePromotion(promotion *Promotion) error - // DeactivatePromotion marks a particular promotion as inactive - DeactivatePromotion(promotion *Promotion) error - // ClaimForWallet is used to either create a new claim or convert a preregistered claim for a particular promotion - ClaimForWallet(promotion *Promotion, issuer *Issuer, wallet *walletutils.Info, blindedCreds jsonutils.JSONStringArray) (*Claim, error) - // CreateClaim is used to "pre-register" an unredeemed claim for a particular wallet - CreateClaim(promotionID uuid.UUID, walletID string, value decimal.Decimal, bonus decimal.Decimal, legacy bool) (*Claim, error) - // GetPreClaim is used to fetch a "pre-registered" claim for a particular wallet - GetPreClaim(promotionID uuid.UUID, walletID string) (*Claim, error) - // CreatePromotion given the promotion type, initial number of grants and the desired value of those grants - CreatePromotion(promotionType string, numGrants int, value decimal.Decimal, platform string) (*Promotion, error) - // GetAvailablePromotionsForWallet returns the list of available promotions for the wallet - GetAvailablePromotionsForWallet(wallet *walletutils.Info, platform string) ([]Promotion, error) - // GetAvailablePromotions returns the list of available promotions for all wallets - GetAvailablePromotions(platform string) ([]Promotion, error) - // GetWithdrawalsAssociated returns the promotion and total amount of claims drained for associated wallets - GetWithdrawalsAssociated(walletID, claimID *uuid.UUID) (*uuid.UUID, decimal.Decimal, error) - // GetPromotionsMissingIssuer returns the list of promotions missing an issuer - GetPromotionsMissingIssuer(limit int) ([]uuid.UUID, error) - // GetClaimCreds returns the claim credentials for a ClaimID - GetClaimCreds(claimID uuid.UUID) (*ClaimCreds, error) - // SaveClaimCreds updates the stored claim credentials - SaveClaimCreds(claimCreds *ClaimCreds) error - // GetPromotion by ID - GetPromotion(promotionID uuid.UUID) (*Promotion, error) - // InsertIssuer inserts the given issuer - InsertIssuer(issuer *Issuer) (*Issuer, error) - // GetIssuer by PromotionID and cohort - GetIssuer(promotionID uuid.UUID, cohort string) (*Issuer, error) - // GetIssuerByPublicKey - GetIssuerByPublicKey(publicKey string) (*Issuer, error) - // GetClaimSummary gets the number of grants for a specific type - GetClaimSummary(walletID uuid.UUID, grantType string) (*ClaimSummary, error) - // GetClaimByWalletAndPromotion gets whether a wallet has a claimed grants - // with the given promotion and returns the grant if so - GetClaimByWalletAndPromotion(wallet *walletutils.Info, promotionID *Promotion) (*Claim, error) - // RunNextClaimJob to sign claim credentials if there is a claim waiting - RunNextClaimJob(ctx context.Context, worker ClaimWorker) (bool, error) - // InsertSuggestion inserts a transaction awaiting validation - InsertSuggestion(credentials []cbr.CredentialRedemption, suggestionText string, suggestion []byte) error - // RunNextSuggestionJob to process a suggestion if there is one waiting - RunNextSuggestionJob(ctx context.Context, worker SuggestionWorker) (bool, error) - // InsertClobberedClaims inserts clobbered claim ids into the clobbered_claims table - InsertClobberedClaims(ctx context.Context, ids []uuid.UUID, version int) error - // InsertBATLossEvent inserts claims of lost bat - InsertBATLossEvent(ctx context.Context, paymentID uuid.UUID, reportID int, amount decimal.Decimal, platform string) (bool, error) - // InsertBAPReportEvent inserts a BAP report - InsertBAPReportEvent(ctx context.Context, paymentID uuid.UUID, amount decimal.Decimal) (*uuid.UUID, error) - - // Remove once this is completed https://github.com/brave-intl/bat-go/issues/263 - - // GetOrder by ID - GetOrder(orderID uuid.UUID) (*Order, error) - // UpdateOrder updates an order when it has been paid - UpdateOrder(orderID uuid.UUID, status string) error - // CreateTransaction creates a transaction - CreateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (*Transaction, error) - // GetSumForTransactions gets a decimal sum of for transactions for an order - GetSumForTransactions(orderID uuid.UUID) (decimal.Decimal, error) -} - -// ReadOnlyDatastore includes all database methods that can be made with a read only db connection -type ReadOnlyDatastore interface { - datastore.Datastore - // GetPreClaim is used to fetch a "pre-registered" claim for a particular wallet - GetPreClaim(promotionID uuid.UUID, walletID string) (*Claim, error) - // GetAvailablePromotionsForWallet returns the list of available promotions for the wallet - GetAvailablePromotionsForWallet(wallet *walletutils.Info, platform string) ([]Promotion, error) - // GetWithdrawalsAssociated returns the promotion and total amount of claims drained for associated wallets - GetWithdrawalsAssociated(walletID, claimID *uuid.UUID) (*uuid.UUID, decimal.Decimal, error) - // GetAvailablePromotions returns the list of available promotions for all wallets - GetAvailablePromotions(platform string) ([]Promotion, error) - // GetPromotionsMissingIssuer returns the list of promotions missing an issuer - GetPromotionsMissingIssuer(limit int) ([]uuid.UUID, error) - // GetClaimCreds returns the claim credentials for a ClaimID - GetClaimCreds(claimID uuid.UUID) (*ClaimCreds, error) - // GetPromotion by ID - GetPromotion(promotionID uuid.UUID) (*Promotion, error) - // GetIssuer by PromotionID and cohort - GetIssuer(promotionID uuid.UUID, cohort string) (*Issuer, error) - // GetIssuerByPublicKey - GetIssuerByPublicKey(publicKey string) (*Issuer, error) - // GetClaimSummary gets the number of grants for a specific type - GetClaimSummary(walletID uuid.UUID, grantType string) (*ClaimSummary, error) - // GetClaimByWalletAndPromotion gets whether a wallet has a claimed grants - // with the given promotion and returns the grant if so - GetClaimByWalletAndPromotion(wallet *walletutils.Info, promotionID *Promotion) (*Claim, error) -} - -// Postgres is a Datastore wrapper around a postgres database -type Postgres struct { - datastore.Postgres -} - -// NewDB creates a new Postgres Datastore -func NewDB(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (Datastore, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if pg != nil { - return &DatastoreWithPrometheus{ - base: &Postgres{*pg}, instanceName: "promotion_datastore", - }, err - } - return nil, err -} - -// NewRODB creates a new Postgres RO Datastore -func NewRODB(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (ReadOnlyDatastore, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if pg != nil { - return &ReadOnlyDatastoreWithPrometheus{ - base: &Postgres{*pg}, instanceName: "promotion_ro_datastore", - }, err - } - return nil, err -} - -// NewPostgres creates new postgres connections -func NewPostgres() (Datastore, ReadOnlyDatastore, error) { - var roPg ReadOnlyDatastore - pg, err := NewDB("", true, "promotion", "promotion_db") - if err != nil { - sentry.CaptureException(err) - log.Panic().Err(err).Msg("Must be able to init postgres connection to start") - } - roDB := os.Getenv("RO_DATABASE_URL") - if len(roDB) > 0 { - roPg, err = NewRODB(roDB, false, "promotion", "promotion_read_only_db") - if err != nil { - sentry.CaptureException(err) - log.Error().Err(err).Msg("Could not start reader postgres connection") - } - } - if roPg == nil { - roPg = pg - } - return pg, roPg, err -} - -// CreatePromotion given the promotion type, initial number of grants and the desired value of those grants -func (pg *Postgres) CreatePromotion(promotionType string, numGrants int, value decimal.Decimal, platform string) (*Promotion, error) { - statement := ` - insert into promotions (promotion_type, remaining_grants, approximate_value, suggestions_per_grant, platform) - values ($1, $2, $3, $4, $5) - returning *` - promotions := []Promotion{} - suggestionsPerGrant := value.Div(defaultVoteValue) - err := pg.RawDB().Select(&promotions, statement, promotionType, numGrants, value, suggestionsPerGrant, platform) - if err != nil { - return nil, err - } - - return &promotions[0], nil -} - -// GetPromotion by ID -func (pg *Postgres) GetPromotion(promotionID uuid.UUID) (*Promotion, error) { - statement := `select *, - case when claimable_until_override is null then created_at + interval '3 months' - else claimable_until_override - end as claimable_until - from promotions where id = $1` - promotions := []Promotion{} - err := pg.RawDB().Select(&promotions, statement, promotionID) - if err != nil { - return nil, err - } - - if len(promotions) > 0 { - return &promotions[0], nil - } - - return nil, nil -} - -// InsertClobberedClaims inserts clobbered claims to the db -func (pg *Postgres) InsertClobberedClaims(ctx context.Context, ids []uuid.UUID, version int) error { - tx, err := pg.RawDB().BeginTxx(ctx, nil) - if err != nil { - return err - } - defer pg.RollbackTx(tx) - - if version < 2 { - // if no version, assume 1 - version = 1 - } - - for _, id := range ids { - _, err = tx.Exec(`INSERT INTO clobbered_claims (id, version) values ($1, $2) ON CONFLICT DO NOTHING;`, id, version) - if err != nil { - return err - } - } - err = tx.Commit() - return err -} - -// DrainTransfer info about the drains -type DrainTransfer struct { - ID *uuid.UUID `db:"transaction_id" json:"transaction_id"` - Total decimal.Decimal `db:"total" json:"total"` - DepositID *string `db:"deposit_destination" json:"deposit_destination"` -} - -// BAPReport holds info about wallet events -type BAPReport struct { - ID uuid.UUID `db:"id" json:"id"` - WalletID uuid.UUID `db:"wallet_id" json:"walletId"` - Amount decimal.Decimal `db:"amount" json:"amount"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` -} - -// InsertBAPReportEvent inserts a BAP report -func (pg *Postgres) InsertBAPReportEvent(ctx context.Context, paymentID uuid.UUID, amount decimal.Decimal) (*uuid.UUID, error) { - - // bap report id - id := uuid.NewV4() - - tx, err := pg.RawDB().BeginTxx(ctx, nil) - if err != nil { - return nil, err - } - defer pg.RollbackTx(tx) - - insertBapReportEventStatement := ` -INSERT INTO bap_report (id, wallet_id, amount) -VALUES ($1, $2, $3)` - - _, err = tx.Exec( - insertBapReportEventStatement, - id, - paymentID, - amount, - ) - if err != nil { - // if this is a duplicate constraint error, conflict propogation - var pgErr *pq.Error - if errors.As(err, &pgErr) { - if pgErr.Code == pq.ErrorCode("23505") { - // duplicate - return nil, errorutils.ErrConflictBAPReportEvent - } - } - return nil, err - } - err = tx.Commit() - return &id, err -} - -// InsertBATLossEvent inserts claims of lost bat to db -func (pg *Postgres) InsertBATLossEvent( - ctx context.Context, - paymentID uuid.UUID, - reportID int, - amount decimal.Decimal, - platform string, -) (bool, error) { - tx, err := pg.RawDB().BeginTxx(ctx, nil) - if err != nil { - return false, err - } - defer pg.RollbackTx(tx) - BATLossEvents := []BATLossEvent{} - - selectStatement := ` -SELECT * -FROM bat_loss_events -WHERE wallet_id = $1 - AND report_id = $2` - - insertBATLossEventStatement := ` -INSERT INTO bat_loss_events (wallet_id, report_id, amount, platform) -VALUES ($1, $2, $3, $4)` - - err = tx.Select( - &BATLossEvents, - selectStatement, - paymentID.String(), - reportID, - ) - if err != nil { - return false, err - } - if len(BATLossEvents) == 0 { - _, err = tx.Exec( - insertBATLossEventStatement, - paymentID.String(), - reportID, - amount, - platform, - ) - if err != nil { - return false, err - } - } else { - if !amount.Equal(BATLossEvents[0].Amount) { - return false, errorutils.ErrConflictBATLossEvent - } - return false, nil - } - err = tx.Commit() - return true, err -} - -// ActivatePromotion marks a particular promotion as active -func (pg *Postgres) ActivatePromotion(promotion *Promotion) error { - return pg.setPromotionActive(promotion, true) -} - -// DeactivatePromotion marks a particular promotion as not active -func (pg *Postgres) DeactivatePromotion(promotion *Promotion) error { - return pg.setPromotionActive(promotion, false) -} - -// setPromotionActive marks a particular promotion's active value -func (pg *Postgres) setPromotionActive(promotion *Promotion, active bool) error { - _, err := pg.RawDB().Exec("update promotions set active = $2 where id = $1", promotion.ID, active) - if err != nil { - return err - } - - return nil -} - -// InsertIssuer inserts the given issuer -func (pg *Postgres) InsertIssuer(issuer *Issuer) (*Issuer, error) { - statement := ` - insert into issuers (promotion_id, cohort, public_key) - values ($1, $2, $3) - returning *` - issuers := []Issuer{} - err := pg.RawDB().Select(&issuers, statement, issuer.PromotionID, issuer.Cohort, issuer.PublicKey) - if err != nil { - return nil, err - } - - if len(issuers) != 1 { - return nil, errors.New("unexpected number of issuers returned") - } - - return &issuers[0], nil -} - -// GetIssuer by PromotionID and cohort -func (pg *Postgres) GetIssuer(promotionID uuid.UUID, cohort string) (*Issuer, error) { - statement := "select * from issuers where promotion_id = $1 and cohort = $2" - issuers := []Issuer{} - err := pg.RawDB().Select(&issuers, statement, promotionID.String(), cohort) - if err != nil { - return nil, err - } - - if len(issuers) > 0 { - return &issuers[0], nil - } - - return nil, nil -} - -// GetIssuerByPublicKey or return an error -func (pg *Postgres) GetIssuerByPublicKey(publicKey string) (*Issuer, error) { - statement := "select * from issuers where public_key = $1" - issuers := []Issuer{} - err := pg.RawDB().Select(&issuers, statement, publicKey) - if err != nil { - return nil, err - } - - if len(issuers) > 0 { - return &issuers[0], nil - } - - return nil, nil -} - -// CreateClaim is used to "pre-register" an unredeemed claim for a particular wallet -func (pg *Postgres) CreateClaim(promotionID uuid.UUID, walletID string, value decimal.Decimal, bonus decimal.Decimal, legacy bool) (*Claim, error) { - statement := ` - insert into claims (promotion_id, wallet_id, approximate_value, bonus, legacy_claimed) - values ($1, $2, $3, $4, $5) - returning *` - claims := []Claim{} - err := pg.RawDB().Select(&claims, statement, promotionID, walletID, value, bonus, legacy) - if err != nil { - return nil, err - } - - return &claims[0], nil -} - -// GetPreClaim is used to fetch a "pre-registered" claim for a particular wallet -func (pg *Postgres) GetPreClaim(promotionID uuid.UUID, walletID string) (*Claim, error) { - claims := []Claim{} - err := pg.RawDB().Select(&claims, "select * from claims where promotion_id = $1 and wallet_id = $2", promotionID.String(), walletID) - if err != nil { - return nil, err - } - - if len(claims) > 0 { - return &claims[0], nil - } - - return nil, nil -} - -// ClaimForWallet is used to either create a new claim or convert a preregistered claim for a particular promotion -func (pg *Postgres) ClaimForWallet(promotion *Promotion, issuer *Issuer, wallet *walletutils.Info, blindedCreds jsonutils.JSONStringArray) (*Claim, error) { - blindedCredsJSON, err := json.Marshal(blindedCreds) - if err != nil { - return nil, err - } - - if promotion.ExpiresAt.Before(time.Now().UTC()) { - return nil, errors.New("unable to claim expired promotion") - } - - tx, err := pg.RawDB().Beginx() - if err != nil { - return nil, err - } - defer pg.RollbackTx(tx) - - claims := []Claim{} - - // Get legacy claims - err = tx.Select(&claims, `select * from claims where legacy_claimed and promotion_id = $1 and wallet_id = $2`, promotion.ID, wallet.ID) - if err != nil { - return nil, err - } - - legacyClaimExists := false - if len(claims) > 1 { - panic("impossible number of claims") - } else if len(claims) == 1 { - legacyClaimExists = true - } - - if !legacyClaimExists { - // This will error if remaining_grants is insufficient due to constraint or the promotion is inactive - res, err := tx.Exec(` - update promotions - set remaining_grants = remaining_grants - 1 - where - id = $1 and - active and - promotions.created_at > NOW() - INTERVAL '3 months'`, - promotion.ID) - - if err != nil { - return nil, err - } - promotionCount, err := res.RowsAffected() - if err != nil { - return nil, err - } else if promotionCount != 1 { - return nil, errors.New("no matching active promotion") - } - } - - claims = []Claim{} - - if promotion.Type == "ads" || legacyClaimExists { - statement := ` - update claims - set redeemed = true, redeemed_at = now() - where promotion_id = $1 and wallet_id = $2 and not redeemed - returning *` - err = tx.Select(&claims, statement, promotion.ID, wallet.ID) - } else { - statement := ` - insert into claims (promotion_id, wallet_id, approximate_value, redeemed, redeemed_at) - values ($1, $2, $3, true, now()) - returning *` - err = tx.Select(&claims, statement, promotion.ID, wallet.ID, promotion.ApproximateValue) - } - - if err != nil { - return nil, err - } else if len(claims) != 1 { - return nil, fmt.Errorf("incorrect number of claims updated / inserted: %d", len(claims)) - } - claim := claims[0] - - // This will error if user has already claimed due to uniqueness constraint - _, err = tx.Exec(`insert into claim_creds (issuer_id, claim_id, blinded_creds, created_at) values ($1, $2, $3, now()) on conflict do nothing`, issuer.ID, claim.ID, blindedCredsJSON) - if err != nil { - return nil, err - } - - err = tx.Commit() - if err != nil { - return nil, err - } - - return &claim, nil -} - -// GetWithdrawalsAssociated returns the promotion and total amount of claims drained for associated wallets -func (pg *Postgres) GetWithdrawalsAssociated(walletID, claimID *uuid.UUID) (*uuid.UUID, decimal.Decimal, error) { - - type associatedWithdrawals struct { - PromotionID *uuid.UUID `db:"promotion_id"` - WithdrawalAmount decimal.Decimal `db:"withdrawal_amount"` - } - - var ( - stmt = ` - select - promotion_id,sum(approximate_value) as withdrawal_amount - from - claims - where - drained=true and - wallet_id in (select wallet_id from wallet_custodian where linking_id in (select linking_id from wallet_custodian where wallet_id = $1 )) and - promotion_id= (select promotion_id from claims where id= $2 limit 1) - group by - promotion_id; - ` - result = new(associatedWithdrawals) - ) - - var err = pg.RawDB().Get(result, stmt, walletID, claimID) - if err != nil { - return nil, decimal.Zero, fmt.Errorf("failed to get withdrawal amount: %w", err) - } - - // TODO: implement, get the promotion id and the total amount withdrawn for associated wallets - return result.PromotionID, result.WithdrawalAmount, nil -} - -// GetAvailablePromotionsForWallet returns the list of available promotions for the wallet -func (pg *Postgres) GetAvailablePromotionsForWallet(wallet *walletutils.Info, platform string) ([]Promotion, error) { - for _, desktopPlatform := range desktopPlatforms { - if platform == desktopPlatform { - platform = "desktop" - } - } - statement := ` - select - promos.id, - promos.promotion_type, - promos.created_at, - case when claimable_until_override is null then promos.created_at + interval '3 months' - else claimable_until_override - end as claimable_until, - promos.expires_at, - promos.version, - coalesce(wallet_claims.approximate_value, promos.approximate_value) as approximate_value, - greatest(1, (coalesce(wallet_claims.approximate_value, promos.approximate_value) / - promos.approximate_value * - promos.suggestions_per_grant - )::int) as suggestions_per_grant, - promos.remaining_grants, - promos.platform, - promos.active, - promos.public_keys, - coalesce(wallet_claims.legacy_claimed, false) as legacy_claimed, - true as available - from - ( - select * from - ( - select - promotion_id, - array_to_json(array_remove(array_agg(public_key), null)) as public_keys - from issuers - group by promotion_id - ) issuer_keys join promotions on promotions.id = issuer_keys.promotion_id - where ( promotions.platform = '' or promotions.platform = $2) - ) promos left join ( - select * from claims where claims.wallet_id = $1 - ) wallet_claims on promos.id = wallet_claims.promotion_id - where - (wallet_claims.redeemed is distinct from true or promos.id='66d1d59f-2c12-44d6-810e-6a3b87fcb9e8' ) and ( - wallet_claims.legacy_claimed is true or ( - promos.created_at > NOW() - INTERVAL '3 months' and promos.active and ( - ( promos.promotion_type = 'ugp' and promos.remaining_grants > 0 ) or - ( promos.promotion_type = 'ads' and wallet_claims.id is not null ) - ) - ) - ) - order by promos.created_at;` - // TODO: remove the promos.id hardcode in 3 months - - promotions := []Promotion{} - - err := pg.RawDB().Select(&promotions, statement, wallet.ID, platform) - if err != nil { - return promotions, err - } - - return promotions, nil -} - -// GetAvailablePromotions returns the list of available promotions for all wallets -func (pg *Postgres) GetAvailablePromotions(platform string) ([]Promotion, error) { - for _, desktopPlatform := range desktopPlatforms { - if platform == desktopPlatform { - platform = "desktop" - } - } - statement := ` - select - promotions.*, - case when promotions.claimable_until_override is null then promotions.created_at + interval '3 months' - else claimable_until_override - end as claimable_until, - false as legacy_claimed, - true as available, - array_to_json(array_remove(array_agg(issuers.public_key), null)) as public_keys - from - promotions left join issuers on promotions.id = issuers.promotion_id - where promotions.promotion_type = 'ugp' and - ( promotions.platform = '' or promotions.platform = $1) and - promotions.active and promotions.remaining_grants > 0 - group by promotions.id - order by promotions.created_at;` - - promotions := []Promotion{} - - err := pg.RawDB().Select(&promotions, statement, platform) - if err != nil { - return promotions, err - } - - return promotions, nil -} - -// GetPromotionsMissingIssuer returns the list of promotions missing an issuer -func (pg *Postgres) GetPromotionsMissingIssuer(limit int) ([]uuid.UUID, error) { - var ( - resp = []uuid.UUID{} - statement = ` - select - promotions.id - from - promotions left join issuers - on promotions.id = issuers.promotion_id - where - issuers.public_key is null - limit $1` - ) - - err := pg.RawDB().Select(&resp, statement, limit) - if err != nil { - return nil, err - } - - return resp, nil -} - -// GetClaimCreds returns the claim credentials for a ClaimID -func (pg *Postgres) GetClaimCreds(claimID uuid.UUID) (*ClaimCreds, error) { - claimCreds := []ClaimCreds{} - err := pg.RawDB().Select(&claimCreds, "select * from claim_creds where claim_id = $1", claimID) - if err != nil { - return nil, err - } - - if len(claimCreds) > 0 { - return &claimCreds[0], nil - } - - return nil, nil -} - -// SaveClaimCreds updates the stored claim credentials -func (pg *Postgres) SaveClaimCreds(creds *ClaimCreds) error { - _, err := pg.RawDB().Exec(`update claim_creds set signed_creds = $1, batch_proof = $2, public_key = $3, updated_at= now() where claim_id = $4`, creds.SignedCreds, creds.BatchProof, creds.PublicKey, creds.ID) - return err -} - -// GetClaimSummary aggregates the values of a single wallet's claims -func (pg *Postgres) GetClaimSummary(walletID uuid.UUID, grantType string) (*ClaimSummary, error) { - statement := ` -select - max(claims.created_at) as "last_claim", - sum(claims.approximate_value - claims.bonus) as earnings, - sum(claims.approximate_value - claims.bonus) as amount, - promos.promotion_type as type -from claims, ( - select - id, - promotion_type - from promotions - where promotion_type = $2 - and id not in (select unnest($3::uuid[])) -) as promos -where claims.wallet_id = $1 - and (claims.redeemed = true or claims.legacy_claimed = true) - and claims.promotion_id = promos.id -group by promos.promotion_type;` - - braveTransferUUIDs, _ := toUUIDs(strings.Split(os.Getenv("BRAVE_TRANSFER_PROMOTION_IDS"), " ")...) - - summaries := []ClaimSummary{} - err := pg.RawDB().Select(&summaries, statement, walletID, grantType, pq.Array(braveTransferUUIDs)) - if err != nil { - return nil, err - } - if len(summaries) > 0 { - return &summaries[0], nil - } - - return nil, nil -} - -// GetClaimByWalletAndPromotion gets whether a wallet has a claimed grants -// with the given promotion and returns the grant if so -func (pg *Postgres) GetClaimByWalletAndPromotion( - wallet *walletutils.Info, - promotion *Promotion, -) (*Claim, error) { - query := ` -SELECT - * -FROM claims - WHERE wallet_id = $1 - AND promotion_id = $2 - AND (legacy_claimed or redeemed) -ORDER BY created_at DESC -` - claims := []Claim{} - err := pg.RawDB().Select(&claims, query, wallet.ID, promotion.ID) - if err != nil { - return nil, err - } - if len(claims) > 0 { - return &claims[0], nil - } - - return nil, nil -} - -// RunNextClaimJob to sign claim credentials if there is a claim waiting, returning true if a job was attempted -func (pg *Postgres) RunNextClaimJob(ctx context.Context, worker ClaimWorker) (bool, error) { - tx, err := pg.RawDB().Beginx() - attempted := false - if err != nil { - return attempted, err - } - defer pg.RollbackTx(tx) - - type SigningJob struct { - Issuer - ClaimID uuid.UUID `db:"claim_id"` - BlindedCreds jsonutils.JSONStringArray `db:"blinded_creds"` - } - - statement := ` -select - issuers.*, - claim_cred.claim_id, - claim_cred.blinded_creds -from - (select * - from claim_creds - where batch_proof is null - for update skip locked - limit 1 -) claim_cred -inner join issuers -on claim_cred.issuer_id = issuers.id` - - jobs := []SigningJob{} - err = tx.Select(&jobs, statement) - if err != nil { - return attempted, err - } - - if len(jobs) != 1 { - return attempted, nil - } - - job := jobs[0] - - attempted = true - creds, err := worker.SignClaimCreds(ctx, job.ClaimID, job.Issuer, job.BlindedCreds) - if err != nil { - // FIXME certain errors are not recoverable - return attempted, err - } - - _, err = tx.Exec(`update claim_creds set signed_creds = $1, batch_proof = $2, public_key = $3, updated_at = now() where claim_id = $4`, - creds.SignedCreds, creds.BatchProof, creds.PublicKey, creds.ID) - if err != nil { - return attempted, err - } - - err = tx.Commit() - if err != nil { - return attempted, err - } - - return attempted, nil -} - -// InsertSuggestion inserts a transaction awaiting validation -func (pg *Postgres) InsertSuggestion(credentials []cbr.CredentialRedemption, suggestionText string, suggestionEvent []byte) error { - credentialsJSON, err := json.Marshal(credentials) - if err != nil { - return err - } - - statement := ` - insert into suggestion_drain (credentials, suggestion_text, suggestion_event) - values ($1, $2, $3) - returning *` - _, err = pg.RawDB().Exec(statement, credentialsJSON, suggestionText, suggestionEvent) - if err != nil { - return err - } - - return nil -} - -// SuggestionJob - representation of a suggestion job -type SuggestionJob struct { - ID uuid.UUID `db:"id"` - Credentials string `db:"credentials"` - SuggestionText string `db:"suggestion_text"` - SuggestionEvent []byte `db:"suggestion_event"` - Erred bool `db:"erred"` - ErrCode *string `db:"errcode"` - CreatedAt time.Time `db:"created_at"` -} - -// RunNextSuggestionJob to process a suggestion if there is one waiting -func (pg *Postgres) RunNextSuggestionJob(ctx context.Context, worker SuggestionWorker) (bool, error) { - - tx, err := pg.RawDB().Beginx() - attempted := false - if err != nil { - return attempted, err - } - defer pg.RollbackTx(tx) - - // FIXME - - statement := ` -select * -from suggestion_drain -where not erred -for update skip locked -limit 1` - - jobs := []SuggestionJob{} - err = tx.Select(&jobs, statement) - if err != nil { - return attempted, err - } - - if len(jobs) != 1 { - return attempted, nil - } - - job := jobs[0] - attempted = true - - var credentials []cbr.CredentialRedemption - err = json.Unmarshal([]byte(job.Credentials), &credentials) - if err != nil { - return attempted, err - } - - // if the error code is "cbr_dup_redeem" we can skip the redeem credentials on drain - // as we are reprocessing a failed job that failed due to duplicate cbr redeem - if job.ErrCode != nil && *job.ErrCode == "cbr_dup_redeem" { - ctx = context.WithValue(ctx, appctx.SkipRedeemCredentialsCTXKey, true) - } - - if !worker.IsPaused() { - err = worker.RedeemAndCreateSuggestionEvent(ctx, credentials, job.SuggestionText, job.SuggestionEvent) - if err != nil { - if strings.Contains(err.Error(), "expired") { - // set flag to stop this worker from running again - worker.PauseWorker(time.Now().Add(30 * time.Minute)) - } - - // add jobID and inform sentry about this error - err = fmt.Errorf("failed to redeem and create suggestion event for jobID %s: %w", job.ID, err) - sentry.CaptureException(err) - - // update suggestion drain as erred - - _, errCode, _ := errToDrainCode(err) - - stmt := "update suggestion_drain set erred = true, errcode = $1 where id = $2" - if _, err := tx.Exec(stmt, errCode, job.ID); err != nil { - return attempted, fmt.Errorf("failed to update errored suggestion job: jobID %s: %w", job.ID, err) - } - - if err := tx.Commit(); err != nil { - return attempted, fmt.Errorf("failed to commit txn for suggestion job: jobID %s: %w", job.ID, err) - } - - return attempted, err - } - - stmt := "delete from suggestion_drain where id = $1" - _, err = tx.Exec(stmt, job.ID) - if err != nil { - return attempted, err - } - - err = tx.Commit() - if err != nil { - return attempted, err - } - } - - return attempted, nil -} - -// This code can be deleted once https://github.com/brave-intl/bat-go/issues/263 is addressed. - -// GetOrder queries the database and returns an order -func (pg *Postgres) GetOrder(orderID uuid.UUID) (*Order, error) { - statement := "SELECT * FROM orders WHERE id = $1" - order := Order{} - err := pg.RawDB().Get(&order, statement, orderID) - if err == sql.ErrNoRows { - return nil, nil - } else if err != nil { - return nil, fmt.Errorf("failed to get order : %w", err) - } - - foundOrderItems := []OrderItem{} - statement = "SELECT * FROM order_items WHERE order_id = $1" - err = pg.RawDB().Select(&foundOrderItems, statement, orderID) - - order.Items = foundOrderItems - if err != nil { - return nil, err - } - - return &order, nil -} - -// UpdateOrder updates the orders status. -// -// Status should either be one of pending, paid, fulfilled, or canceled. -func (pg *Postgres) UpdateOrder(orderID uuid.UUID, status string) error { - result, err := pg.RawDB().Exec(`UPDATE orders set status = $1, updated_at = CURRENT_TIMESTAMP where id = $2`, status, orderID) - - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if rowsAffected == 0 || err != nil { - return errors.New("no rows updated") - } - - return nil -} - -// CreateTransaction creates a transaction given an orderID, externalTransactionID, currency, and a kind of transaction -func (pg *Postgres) CreateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (*Transaction, error) { - tx := pg.RawDB().MustBegin() - defer pg.RollbackTx(tx) - - var transaction Transaction - err := tx.Get(&transaction, - ` - INSERT INTO transactions (order_id, external_transaction_id, status, currency, kind, amount) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING * - `, orderID, externalTransactionID, status, currency, kind, amount) - - if err != nil { - return nil, err - } - - err = tx.Commit() - if err != nil { - return nil, err - } - - return &transaction, nil -} - -// GetSumForTransactions returns the calculated sum -func (pg *Postgres) GetSumForTransactions(orderID uuid.UUID) (decimal.Decimal, error) { - var sum decimal.Decimal - - err := pg.RawDB().Get(&sum, ` - SELECT SUM(amount) as sum - FROM transactions - WHERE order_id = $1 AND status = 'completed' - `, orderID) - - return sum, err -} - -// UpdateDrainJobAsRetriable - updates a drain job as retriable -func (pg *Postgres) UpdateDrainJobAsRetriable(ctx context.Context, walletID uuid.UUID) error { - query := ` - UPDATE claim_drain - SET erred = FALSE, status = 'manual-retry' - WHERE wallet_id = $1 AND erred = TRUE AND status IN ('reputation-failed', 'failed') AND transaction_id IS NULL - ` - result, err := pg.ExecContext(ctx, query, walletID) - if err != nil { - return fmt.Errorf("update drain job: failed to exec update for walletID %s: %w", walletID, err) - } - - affectedRows, err := result.RowsAffected() - if err != nil { - return fmt.Errorf("update drain job: failed to get affected rows for walletID %s: %w", walletID, err) - } - - if affectedRows == 0 { - return fmt.Errorf("update drain job: failed to update row for walletID %s: %w", walletID, - errorutils.ErrNotFound) - } - - return nil -} - -func toUUIDs(a ...string) ([]uuid.UUID, error) { - var ( - b = []uuid.UUID{} - ) - for _, id := range a { - v, err := uuid.FromString(id) - if err != nil { - return nil, err - } - b = append(b, v) - } - return b, nil -} - -// errToDrainCode - given a drain related processing error, generate a code and retriable flag -func errToDrainCode(err error) (string, string, bool) { - var ( - status string - errCode string - retriable bool - ) - - if err == nil { - return "", "", false - } - - status = "failed" - - var eb *errorutils.ErrorBundle - if errors.As(err, &eb) { - // if this is an error bundle, check the "data" for a codified type - if c, ok := eb.Data().(errorutils.Codified); ok { - errCode, retriable = c.DrainCode() - return status, strings.ToLower(errCode), retriable - } else if c, ok := eb.Data().(errorutils.DrainCodified); ok { - errCode, retriable = c.DrainCode() - return status, strings.ToLower(errCode), retriable - } - } - - // possible protocol errors - if errors.Is(err, errorutils.ErrMarshalTransferRequest) { - errCode = "marshal_transfer" - } else if errors.Is(err, errorutils.ErrCreateTransferRequest) { - errCode = "create_transfer" - } else if errors.Is(err, errorutils.ErrSignTransferRequest) { - errCode = "sign_transfer" - } else if errors.Is(err, errorutils.ErrFailedClientRequest) { - errCode = "failed_client" - retriable = true - } else if errors.Is(err, errorutils.ErrFailedBodyRead) { - errCode = "failed_response_body" - retriable = true - } else if errors.Is(err, errorutils.ErrFailedBodyUnmarshal) { - errCode = "failed_response_unmarshal" - retriable = true - } else if errors.Is(err, errReputationServiceFailure) { - errCode = "reputation-service-failure" - retriable = true - } else if errors.Is(err, errWalletNotReputable) { - errCode = "reputation-failed" - status = "reputation-failed" - retriable = false - } else if errors.Is(err, errWalletDrainLimitExceeded) { - errCode = "exceeded-withdrawal-limit" - status = "exceeded-withdrawal-limit" - retriable = false - } else { - errCode = "unknown" - var bfe *clients.BitflyerError - if errors.As(err, &bfe) { - // possible wallet provider specific errors - if len(bfe.ErrorIDs) > 0 { - errCode = fmt.Sprintf("bitflyer_%s", bfe.ErrorIDs[0]) - } - } - } - return status, strings.ToLower(errCode), retriable -} diff --git a/services/promotion/datastore_test.go b/services/promotion/datastore_test.go deleted file mode 100644 index 2ad4f17e8..000000000 --- a/services/promotion/datastore_test.go +++ /dev/null @@ -1,756 +0,0 @@ -//go:build integration - -package promotion - -import ( - "context" - "errors" - - "github.com/brave-intl/bat-go/libs/jsonutils" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/services/wallet" - "github.com/golang/mock/gomock" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type PostgresTestSuite struct { - suite.Suite -} - -func (suite *PostgresTestSuite) SetupSuite() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - m, err := pg.NewMigrate() - suite.Require().NoError(err, "Failed to create migrate instance") - - ver, dirty, _ := m.Version() - if dirty { - suite.Require().NoError(m.Force(int(ver))) - } - if ver > 0 { - suite.Require().NoError(m.Down(), "Failed to migrate down cleanly") - } - - suite.Require().NoError(pg.Migrate(), "Failed to fully migrate") -} - -func (suite *PostgresTestSuite) SetupTest() { - suite.CleanDB() -} - -func (suite *PostgresTestSuite) TearDownTest() { - suite.CleanDB() -} - -func (suite *PostgresTestSuite) CleanDB() { - tables := []string{"claim_creds", "claims", "wallets", "issuers", "promotions", "claim_drain"} - - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - for _, table := range tables { - _, err = pg.RawDB().Exec("delete from " + table) - suite.Require().NoError(err, "Failed to get clean table") - } -} - -func (suite *PostgresTestSuite) TestCreatePromotion() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - value := decimal.NewFromFloat(25.0) - numGrants := 10 - - promotion, err := pg.CreatePromotion("ugp", numGrants, value, "") - suite.Require().NoError(err, "Create promotion should succeed") - - suite.Require().Equal(numGrants, promotion.RemainingGrants) - suite.Require().True(value.Equal(promotion.ApproximateValue)) -} - -func (suite *PostgresTestSuite) TestGetPromotion() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - value := decimal.NewFromFloat(25.0) - numGrants := 10 - - promotion, err := pg.CreatePromotion("ugp", numGrants, value, "") - suite.Require().NoError(err, "Create promotion should succeed") - - promotion, err = pg.GetPromotion(promotion.ID) - suite.Require().NoError(err, "Get promotion should succeed") - - suite.Assert().Equal(numGrants, promotion.RemainingGrants) - suite.Assert().True(value.Equal(promotion.ApproximateValue)) -} - -func (suite *PostgresTestSuite) TestActivatePromotion() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - promotion, err := pg.CreatePromotion("ugp", 1, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - suite.Assert().False(promotion.Active) - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - promotion, err = pg.GetPromotion(promotion.ID) - suite.Require().NoError(err, "Get promotion should succeed") - - suite.Assert().True(promotion.Active) -} - -func (suite *PostgresTestSuite) TestInsertIssuer() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - promotion, err := pg.CreatePromotion("ugp", 10, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "test", PublicKey: publicKey} - _, err = pg.InsertIssuer(issuer) - - suite.Require().NoError(err, "Save issuer should succeed") -} - -func (suite *PostgresTestSuite) TestGetIssuer() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - promotion, err := pg.CreatePromotion("ugp", 10, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - origIssuer := &Issuer{PromotionID: promotion.ID, Cohort: "test", PublicKey: publicKey} - origIssuer, err = pg.InsertIssuer(origIssuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - issuerByPromoAndCohort, err := pg.GetIssuer(promotion.ID, "test") - suite.Require().NoError(err, "Get issuer should succeed") - suite.Assert().Equal(origIssuer, issuerByPromoAndCohort) - - issuerByPublicKey, err := pg.GetIssuerByPublicKey(publicKey) - suite.Require().NoError(err, "Get issuer by public key should succeed") - suite.Assert().Equal(origIssuer, issuerByPublicKey) -} - -func (suite *PostgresTestSuite) TestCreateClaim() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - promotion, err := pg.CreatePromotion("ads", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - _, err = pg.CreateClaim(promotion.ID, w.ID, decimal.NewFromFloat(30.0), decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") -} - -func (suite *PostgresTestSuite) TestGetPreClaim() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - promotion, err := pg.CreatePromotion("ads", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - expectedClaim, err := pg.CreateClaim(promotion.ID, w.ID, decimal.NewFromFloat(30.0), decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") - - claim, err := pg.GetPreClaim(promotion.ID, w.ID) - suite.Require().NoError(err, "Create promotion should succeed") - suite.Assert().Equal(expectedClaim, claim) -} - -func (suite *PostgresTestSuite) TestClaimForWallet() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - blindedCreds := jsonutils.JSONStringArray([]string{}) - - promotion, err := pg.CreatePromotion("ugp", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().Error(err, "Claim for wallet should fail, promotion is not active") - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().NoError(err, "Claim for wallet should succeed, promotion is active and has grants left") - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().Error(err, "Claim for wallet should fail, wallet already claimed this promotion") - - w = &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().NoError(err, "Claim for wallet should succeed, promotion is active and has grants left") - - w = &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().Error(err, "Claim for wallet should fail, promotion is active but has no more grants") - - promotion, err = pg.CreatePromotion("ads", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - w = &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - _, err = pg.CreateClaim(promotion.ID, w.ID, decimal.NewFromFloat(30.0), decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") - - w2 := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w2), "Save wallet should succeed") - _, err = pg.ClaimForWallet(promotion, issuer, w2, blindedCreds) - suite.Require().Error(err, "Claim for wallet should fail, wallet does not have pre-registered claim") - - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().NoError(err, "Claim for wallet should succeed, wallet has pre-registered claim") - - promotion, err = pg.GetPromotion(promotion.ID) - suite.Require().NoError(err, "Get promotion should succeed") - suite.Assert().Equal(1, promotion.RemainingGrants) -} - -func (suite *PostgresTestSuite) TestGetAvailablePromotionsForWallet() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - promotions, err := pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - promotion, err := pg.CreatePromotion("ugp", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - promotion.PublicKeys = jsonutils.JSONStringArray{} - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) - suite.Assert().True(promotions[0].Active) - suite.Assert().True(promotions[0].Available) - - promotion, err = pg.CreatePromotion("ads", 2, decimal.NewFromFloat(35.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - issuer = &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) - suite.Assert().True(promotions[0].Available) - - // 30.7 * 4 = 122.8 => test differences in rounding - adClaimValue := decimal.NewFromFloat(30.7) - claim, err := pg.CreateClaim(promotion.ID, w.ID, adClaimValue, decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") - adSuggestionsPerGrant, err := claim.SuggestionsNeeded(promotion) - suite.Require().NoError(err) - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(2, len(promotions)) - suite.Assert().True(promotions[0].Available) - suite.Assert().True(promotions[1].Available) - suite.Assert().True(adClaimValue.Equals(promotions[1].ApproximateValue)) - suite.Assert().Equal(adSuggestionsPerGrant, promotions[1].SuggestionsPerGrant) - - promotion, err = pg.CreatePromotion("ads", 2, decimal.NewFromFloat(35.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - issuer = &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - // test when claim is for less than the value of one vote - adClaimValue = decimal.NewFromFloat(0.05) - claim, err = pg.CreateClaim(promotion.ID, w.ID, adClaimValue, decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") - adSuggestionsPerGrant, err = claim.SuggestionsNeeded(promotion) - suite.Require().NoError(err) - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(3, len(promotions)) - suite.Assert().True(promotions[0].Available) - suite.Assert().True(promotions[1].Available) - suite.Assert().True(promotions[2].Available) - suite.Assert().True(adClaimValue.Equals(promotions[2].ApproximateValue)) - suite.Assert().Equal(adSuggestionsPerGrant, promotions[2].SuggestionsPerGrant) - suite.Assert().Equal(1, adSuggestionsPerGrant) -} - -func (suite *PostgresTestSuite) TestGetAvailablePromotions() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - promotions, err := pg.GetAvailablePromotions("") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - promotion, err := pg.CreatePromotion("ugp", 0, decimal.NewFromFloat(15.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - promotion.PublicKeys = jsonutils.JSONStringArray{} - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - promotions, err = pg.GetAvailablePromotions("") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions), "Active promo with no grants should not appears in legacy list") - - suite.CleanDB() - - promotion, err = pg.CreatePromotion("ugp", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - promotion.PublicKeys = jsonutils.JSONStringArray{} - - promotions, err = pg.GetAvailablePromotions("") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - promotions, err = pg.GetAvailablePromotions("") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) - suite.Assert().True(promotions[0].Active) - suite.Assert().True(promotions[0].Available) - - promotion, err = pg.CreatePromotion("ads", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - promotions, err = pg.GetAvailablePromotions("") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) - suite.Assert().True(promotions[0].Active) - suite.Assert().True(promotions[0].Available) - - // Test platform='desktop' returns all desktop grants for non-legacy - // GetAvailablePromotions endpoint w/o walletID - suite.CleanDB() - - // Create desktop promotion - promotion, err = pg.CreatePromotion("ugp", 1, decimal.NewFromFloat(25.0), "desktop") - suite.Require().NoError(err, "Create promotion should succeed") - err = pg.ActivatePromotion(promotion) - suite.Require().NoError(err, "Activate promotion should succeed") - - // Ensure they are all returned - promotions, err = pg.GetAvailablePromotions("desktop") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - promotions, err = pg.GetAvailablePromotions("osx") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - promotions, err = pg.GetAvailablePromotions("linux") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - promotions, err = pg.GetAvailablePromotions("windows") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - // Test platform='desktop' returns all desktop grants for legacy - // GetAvailablePromotions endpoint without walletID - suite.CleanDB() - - promotion, err = pg.CreatePromotion("ugp", 1, decimal.NewFromFloat(25.0), "desktop") - suite.Require().NoError(err, "Create promotion should succeed") - err = pg.ActivatePromotion(promotion) - suite.Require().NoError(err, "Activate promotion should succeed") - - // Ensure they are all returned - // Legacy endpoints only return active - err = pg.ActivatePromotion(promotion) - suite.Require().NoError(err, "Activate promotion should succeed") - - promotions, err = pg.GetAvailablePromotions("desktop") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - promotions, err = pg.GetAvailablePromotions("osx") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - promotions, err = pg.GetAvailablePromotions("linux") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - promotions, err = pg.GetAvailablePromotions("windows") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(len(promotions), 1) - - suite.CleanDB() - - // Create desktop promotion - promotion, err = pg.CreatePromotion("ugp", 1, decimal.NewFromFloat(25.0), "ios") - suite.Require().NoError(err, "Create promotion should succeed") - - // it should not be in the list until activated - promotions, err = pg.GetAvailablePromotions("ios") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - err = pg.ActivatePromotion(promotion) - - promotions, err = pg.GetAvailablePromotions("ios") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) - - // Desktop should not see an iOS grant - promotions, err = pg.GetAvailablePromotions("desktop") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - // But iOS should - promotions, err = pg.GetAvailablePromotions("ios") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) -} - -func (suite *PostgresTestSuite) TestGetAvailablePromotionsForWalletLegacy() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - w2 := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w2), "Save wallet should succeed") - - // create an ancient promotion to make sure no new claims can be made on it - ancient_promotion, err := pg.CreatePromotion("ugp", 1, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create Promotion should succeed") - changed, err := pg.RawDB().Exec(` - update promotions set created_at= NOW() - INTERVAL '4 months' where id=$1`, ancient_promotion.ID) - suite.Require().NoError(err, "should be able to set the promotion created_at to 4 months ago") - changed_rows, _ := changed.RowsAffected() - suite.Assert().Equal(int64(1), changed_rows) - - // at this point the promotion should no longer show up for the wallet, making the list empty below: - promotions, err := pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions)) - - promotion, err := pg.CreatePromotion("ugp", 1, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions), "Legacy listing should not show inactive promotions") - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions)) - suite.Assert().True(promotions[0].Active) - suite.Assert().True(promotions[0].Available) - - // Simulate legacy claim - claim, err := pg.CreateClaim(promotion.ID, w.ID, decimal.NewFromFloat(25.0), decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating claim should succeed") - _, err = pg.RawDB().Exec("update claims set legacy_claimed = true where claims.id = $1", claim.ID) - suite.Require().NoError(err, "Setting legacy_claimed should succeed") - _, err = pg.RawDB().Exec(`update promotions set remaining_grants = remaining_grants - 1 where id = $1 and active`, promotion.ID) - suite.Require().NoError(err, "Setting remaining grants should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions), "Legacy claimed promotions should appear in non-legacy list") - suite.Assert().True(promotions[0].Active) - suite.Assert().True(promotions[0].Available) - - promotions, err = pg.GetAvailablePromotionsForWallet(w2, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(0, len(promotions), "Promotion with one grant should not appear after one claim") - - promotion, err = pg.CreatePromotion("ads", 1, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - issuer = &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(1, len(promotions), "Unavailable ads promo should not appear") - - // Create pre-registered ads claim - claim, err = pg.CreateClaim(promotion.ID, w.ID, decimal.NewFromFloat(30.0), decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(2, len(promotions)) - suite.Assert().True(promotions[1].Available) - - // Simulate legacy claim - _, err = pg.RawDB().Exec("update claims set legacy_claimed = true where claims.id = $1", claim.ID) - suite.Require().NoError(err, "Setting legacy_claimed should succeed") - _, err = pg.RawDB().Exec(`update promotions set remaining_grants = remaining_grants - 1 where id = $1 and active`, promotion.ID) - suite.Require().NoError(err, "Setting remaining grants should succeed") - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(2, len(promotions), "Legacy claimed promotions should appear in non-legacy list") - suite.Assert().True(promotions[0].Available) - suite.Assert().True(promotions[1].Available) - - // Deactivate a promotion - suite.Require().NoError(pg.DeactivatePromotion(&promotions[0])) - - promotions, err = pg.GetAvailablePromotionsForWallet(w, "") - suite.Require().NoError(err, "Get promotions should succeed") - suite.Assert().Equal(2, len(promotions), "Deactivated legacy claimed promotions should appear in the non-legacy list") -} - -func (suite *PostgresTestSuite) TestGetClaimCreds() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - blindedCreds := jsonutils.JSONStringArray([]string{"hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY="}) - - promotion, err := pg.CreatePromotion("ugp", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - claim, err := pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().NoError(err, "Claim for wallet should succeed, promotion is active and has grants left") - - claimCreds, err := pg.GetClaimCreds(claim.ID) - suite.Require().NoError(err, "Get claim creds should succeed") - - suite.Assert().Equal(blindedCreds, claimCreds.BlindedCreds) -} - -func (suite *PostgresTestSuite) TestGetClaimByWalletAndPromotion() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - blindedCreds := jsonutils.JSONStringArray([]string{"hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY="}) - w := &walletutils.Info{ - ID: uuid.NewV4().String(), - Provider: "uphold", - ProviderID: uuid.NewV4().String(), - PublicKey: publicKey, - } - err = walletDB.UpsertWallet(context.Background(), w) - - // Create promotion - promotion, err := pg.CreatePromotion( - "ugp", - 2, - decimal.NewFromFloat(50.0), - "", - ) - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - _, err = pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().NoError(err, "Claim creation should succeed") - - // First try to look up a a claim for a wallet that doesn't have one - fakeWallet := &walletutils.Info{ID: uuid.NewV4().String()} - claim, err := pg.GetClaimByWalletAndPromotion(fakeWallet, promotion) - suite.Require().NoError(err, "Get claim by wallet and promotion should succeed") - suite.Assert().Nil(claim) - - // Now look up claim for wallet that does have one - claim, err = pg.GetClaimByWalletAndPromotion(w, promotion) - suite.Require().NoError(err, "Get claim by wallet and promotion should succeed") - suite.Assert().Equal(claim.PromotionID, promotion.ID) - suite.Assert().Equal(claim.WalletID.String(), w.ID) - - promotion, err = pg.CreatePromotion("ads", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - _, err = pg.CreateClaim(promotion.ID, w.ID, decimal.NewFromFloat(30.0), decimal.NewFromFloat(0), false) - suite.Require().NoError(err, "Creating pre-registered claim should succeed") - - // A preregistered claim should not exist - claim, err = pg.GetClaimByWalletAndPromotion(w, promotion) - suite.Require().NoError(err, "Get claim by wallet and promotion should succeed") - suite.Assert().Nil(claim) -} - -func (suite *PostgresTestSuite) TestSaveClaimCreds() { - // FIXME -} - -func (suite *PostgresTestSuite) TestRunNextClaimJob() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - mockClaimWorker := NewMockClaimWorker(mockCtrl) - - attempted, err := pg.RunNextClaimJob(context.Background(), mockClaimWorker) - suite.Assert().Equal(false, attempted) - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - blindedCreds := jsonutils.JSONStringArray([]string{"hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY="}) - signedCreds := jsonutils.JSONStringArray([]string{"hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY="}) - batchProof := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - promotion, err := pg.CreatePromotion("ugp", 2, decimal.NewFromFloat(25.0), "") - suite.Require().NoError(err, "Create promotion should succeed") - - issuer := &Issuer{PromotionID: promotion.ID, Cohort: "control", PublicKey: publicKey} - issuer, err = pg.InsertIssuer(issuer) - suite.Require().NoError(err, "Insert issuer should succeed") - - w := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(walletDB.UpsertWallet(context.Background(), w), "Save wallet should succeed") - - suite.Require().NoError(pg.ActivatePromotion(promotion), "Activate promotion should succeed") - - claim, err := pg.ClaimForWallet(promotion, issuer, w, blindedCreds) - suite.Require().NoError(err, "Claim for wallet should succeed, promotion is active and has grants left") - - creds := &ClaimCreds{ - ID: claim.ID, - BlindedCreds: blindedCreds, - SignedCreds: &signedCreds, - BatchProof: &batchProof, - PublicKey: &issuer.PublicKey, - } - - // One signing job should run - mockClaimWorker.EXPECT().SignClaimCreds(gomock.Any(), gomock.Eq(claim.ID), gomock.Eq(*issuer), gomock.Eq([]string(blindedCreds))).Return(nil, errors.New("Worker failed")) - attempted, err = pg.RunNextClaimJob(context.Background(), mockClaimWorker) - suite.Assert().Equal(true, attempted) - suite.Require().Error(err) - - // Signing job should rerun on failure - mockClaimWorker.EXPECT().SignClaimCreds(gomock.Any(), gomock.Eq(claim.ID), gomock.Eq(*issuer), gomock.Eq([]string(blindedCreds))).Return(creds, nil) - attempted, err = pg.RunNextClaimJob(context.Background(), mockClaimWorker) - suite.Assert().Equal(true, attempted) - suite.Require().NoError(err) - - // No further jobs should run after success - attempted, err = pg.RunNextClaimJob(context.Background(), mockClaimWorker) - suite.Assert().Equal(false, attempted) - suite.Require().NoError(err) -} - -func (suite *PostgresTestSuite) TestInsertClobberedClaims() { - ctx := context.Background() - id1 := uuid.NewV4() - id2 := uuid.NewV4() - - pg, _, err := NewPostgres() - suite.Assert().NoError(err) - suite.Require().NoError(pg.InsertClobberedClaims(ctx, []uuid.UUID{id1, id2}, 1), "Create promotion should succeed") - - var allCreds1 []ClobberedCreds - var allCreds2 []ClobberedCreds - err = pg.RawDB().Select(&allCreds1, `select * from clobbered_claims;`) - suite.Require().NoError(err, "selecting the clobbered creds ids should not result in an error") - - suite.Require().NoError(pg.InsertClobberedClaims(ctx, []uuid.UUID{id1, id2}, 1), "Create promotion should succeed") - err = pg.RawDB().Select(&allCreds2, `select * from clobbered_claims;`) - suite.Require().NoError(err, "selecting the clobbered creds ids should not result in an error") - suite.Assert().Equal(allCreds1, allCreds2, "creds should not be inserted more than once") -} diff --git a/services/promotion/drain.go b/services/promotion/drain.go deleted file mode 100644 index 90032407d..000000000 --- a/services/promotion/drain.go +++ /dev/null @@ -1,97 +0,0 @@ -package promotion - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "github.com/brave-intl/bat-go/libs/logging" - "github.com/lib/pq" - "github.com/prometheus/client_golang/prometheus" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -var ( - errReputationServiceFailure = errors.New("failed to call reputation service") - errWalletNotReputable = errors.New("wallet is not reputable") - errWalletDrainLimitExceeded = errors.New("wallet drain limit exceeded") - withdrawalLimitHit = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "withdrawalLimitHit", - Help: "A counter for when a drain hits the withdrawal limit", - ConstLabels: prometheus.Labels{"service": "wallet"}, - }) -) - -// MintGrant create a new grant for the wallet specified with the total specified -func (service *Service) MintGrant(ctx context.Context, walletID uuid.UUID, total decimal.Decimal, promotions ...uuid.UUID) error { - // setup a logger - logger := logging.Logger(ctx, "promotion.MintGrant") - - // for all of the promotion ids (limit of 4 wallets can be linked) - // attempt to create a claim. If we run into a unique key constraint, this means that - // we have already created a claim for this wallet id/ promotion - var attempts int - for _, pID := range promotions { - logger.Debug().Msg("MintGrant: creating the claim to destination") - // create a new claim for the wallet deposit account for total - // this is a legacy claimed claim - _, err := service.Datastore.CreateClaim(pID, walletID.String(), total, decimal.Zero, true) - if err != nil { - var pgErr *pq.Error - if errors.As(err, &pgErr) { - // unique constraint error (wallet id and promotion id combo exists) - // use one of the other 4 promotions instead - if pgErr.Code == "23505" { - attempts++ - continue - } - } - logger.Error().Err(err).Msg("MintGrant: failed to create a new claim to destination") - return err - } - break - } - if attempts >= len(promotions) { - return errors.New("limit of draining 4 wallets to brave wallet exceeded") - } - return nil -} - -// FetchAdminAttestationWalletID - retrieves walletID from topic -func (service *Service) FetchAdminAttestationWalletID(ctx context.Context) (*uuid.UUID, error) { - message, err := service.kafkaAdminAttestationReader.ReadMessage(ctx) - if err != nil { - return nil, fmt.Errorf("read message: error reading kafka message %w", err) - } - - codec, ok := service.codecs[adminAttestationTopic] - if !ok { - return nil, fmt.Errorf("read message: could not find codec %s", adminAttestationTopic) - } - - native, _, err := codec.NativeFromBinary(message.Value) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode naitve from binary %w", err) - } - - textual, err := codec.TextualFromNative(nil, native) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode textual from native %w", err) - } - - var adminAttestationEvent AdminAttestationEvent - err = json.Unmarshal(textual, &adminAttestationEvent) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode json from textual %w", err) - } - - walletID := uuid.FromStringOrNil(adminAttestationEvent.WalletID) - if walletID == uuid.Nil { - return nil, fmt.Errorf("read message: error could not decode walletID %s", adminAttestationEvent.WalletID) - } - - return &walletID, nil -} diff --git a/services/promotion/drain_info.go b/services/promotion/drain_info.go deleted file mode 100644 index 2c56e6870..000000000 --- a/services/promotion/drain_info.go +++ /dev/null @@ -1,18 +0,0 @@ -package promotion - -import ( - "time" - - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// DrainInfo - generic custodian output data -type DrainInfo struct { - PromotionID *uuid.UUID `json:"promotion_id,omitempty" db:"promotion_id"` - TransactionID *uuid.UUID `json:"transaction_id,omitempty" db:"transaction_id"` - CompletedAt *time.Time `json:"completed_at,omitempty" db:"completed_at"` - State *string `json:"state,omitempty" db:"state"` - ErrCode *string `json:"errcode,omitempty" db:"errcode"` - Value decimal.Decimal `json:"value,omitempty" db:"value"` -} diff --git a/services/promotion/drain_test.go b/services/promotion/drain_test.go deleted file mode 100644 index 8b52434be..000000000 --- a/services/promotion/drain_test.go +++ /dev/null @@ -1 +0,0 @@ -package promotion diff --git a/services/promotion/instrumented_datastore.go b/services/promotion/instrumented_datastore.go deleted file mode 100755 index 3a25f69bd..000000000 --- a/services/promotion/instrumented_datastore.go +++ /dev/null @@ -1,515 +0,0 @@ -package promotion - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/promotion -i Datastore -t ../../.prom-gowrap.tmpl -o instrumented_datastore.go -l "" - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/clients/cbr" - "github.com/brave-intl/bat-go/libs/jsonutils" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// DatastoreWithPrometheus implements Datastore interface with all methods wrapped -// with Prometheus metrics -type DatastoreWithPrometheus struct { - base Datastore - instanceName string -} - -var datastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "promotion_datastore_duration_seconds", - Help: "datastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewDatastoreWithPrometheus returns an instance of the Datastore decorated with prometheus summary metric -func NewDatastoreWithPrometheus(base Datastore, instanceName string) DatastoreWithPrometheus { - return DatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// ActivatePromotion implements Datastore -func (_d DatastoreWithPrometheus) ActivatePromotion(promotion *Promotion) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "ActivatePromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.ActivatePromotion(promotion) -} - -// BeginTx implements Datastore -func (_d DatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// ClaimForWallet implements Datastore -func (_d DatastoreWithPrometheus) ClaimForWallet(promotion *Promotion, issuer *Issuer, wallet *walletutils.Info, blindedCreds jsonutils.JSONStringArray) (cp1 *Claim, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "ClaimForWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.ClaimForWallet(promotion, issuer, wallet, blindedCreds) -} - -// CreateClaim implements Datastore -func (_d DatastoreWithPrometheus) CreateClaim(promotionID uuid.UUID, walletID string, value decimal.Decimal, bonus decimal.Decimal, legacy bool) (cp1 *Claim, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateClaim", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateClaim(promotionID, walletID, value, bonus, legacy) -} - -// CreatePromotion implements Datastore -func (_d DatastoreWithPrometheus) CreatePromotion(promotionType string, numGrants int, value decimal.Decimal, platform string) (pp1 *Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CreatePromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreatePromotion(promotionType, numGrants, value, platform) -} - -// CreateTransaction implements Datastore -func (_d DatastoreWithPrometheus) CreateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (tp1 *Transaction, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateTransaction", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateTransaction(orderID, externalTransactionID, status, currency, kind, amount) -} - -// DeactivatePromotion implements Datastore -func (_d DatastoreWithPrometheus) DeactivatePromotion(promotion *Promotion) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "DeactivatePromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.DeactivatePromotion(promotion) -} - -// GetAvailablePromotions implements Datastore -func (_d DatastoreWithPrometheus) GetAvailablePromotions(platform string) (pa1 []Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetAvailablePromotions", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetAvailablePromotions(platform) -} - -// GetAvailablePromotionsForWallet implements Datastore -func (_d DatastoreWithPrometheus) GetAvailablePromotionsForWallet(wallet *walletutils.Info, platform string) (pa1 []Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetAvailablePromotionsForWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetAvailablePromotionsForWallet(wallet, platform) -} - -// GetClaimByWalletAndPromotion implements Datastore -func (_d DatastoreWithPrometheus) GetClaimByWalletAndPromotion(wallet *walletutils.Info, promotionID *Promotion) (cp1 *Claim, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetClaimByWalletAndPromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetClaimByWalletAndPromotion(wallet, promotionID) -} - -// GetClaimCreds implements Datastore -func (_d DatastoreWithPrometheus) GetClaimCreds(claimID uuid.UUID) (cp1 *ClaimCreds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetClaimCreds", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetClaimCreds(claimID) -} - -// GetClaimSummary implements Datastore -func (_d DatastoreWithPrometheus) GetClaimSummary(walletID uuid.UUID, grantType string) (cp1 *ClaimSummary, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetClaimSummary", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetClaimSummary(walletID, grantType) -} - -// GetIssuer implements Datastore -func (_d DatastoreWithPrometheus) GetIssuer(promotionID uuid.UUID, cohort string) (ip1 *Issuer, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuer(promotionID, cohort) -} - -// GetIssuerByPublicKey implements Datastore -func (_d DatastoreWithPrometheus) GetIssuerByPublicKey(publicKey string) (ip1 *Issuer, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuerByPublicKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuerByPublicKey(publicKey) -} - -// GetOrder implements Datastore -func (_d DatastoreWithPrometheus) GetOrder(orderID uuid.UUID) (op1 *Order, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOrder(orderID) -} - -// GetPreClaim implements Datastore -func (_d DatastoreWithPrometheus) GetPreClaim(promotionID uuid.UUID, walletID string) (cp1 *Claim, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPreClaim", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPreClaim(promotionID, walletID) -} - -// GetPromotion implements Datastore -func (_d DatastoreWithPrometheus) GetPromotion(promotionID uuid.UUID) (pp1 *Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPromotion(promotionID) -} - -// GetPromotionsMissingIssuer implements Datastore -func (_d DatastoreWithPrometheus) GetPromotionsMissingIssuer(limit int) (ua1 []uuid.UUID, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPromotionsMissingIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPromotionsMissingIssuer(limit) -} - -// GetSumForTransactions implements Datastore -func (_d DatastoreWithPrometheus) GetSumForTransactions(orderID uuid.UUID) (d1 decimal.Decimal, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetSumForTransactions", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetSumForTransactions(orderID) -} - -// GetWithdrawalsAssociated implements Datastore -func (_d DatastoreWithPrometheus) GetWithdrawalsAssociated(walletID *uuid.UUID, claimID *uuid.UUID) (up1 *uuid.UUID, d1 decimal.Decimal, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetWithdrawalsAssociated", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetWithdrawalsAssociated(walletID, claimID) -} - -// InsertBAPReportEvent implements Datastore -func (_d DatastoreWithPrometheus) InsertBAPReportEvent(ctx context.Context, paymentID uuid.UUID, amount decimal.Decimal) (up1 *uuid.UUID, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertBAPReportEvent", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertBAPReportEvent(ctx, paymentID, amount) -} - -// InsertBATLossEvent implements Datastore -func (_d DatastoreWithPrometheus) InsertBATLossEvent(ctx context.Context, paymentID uuid.UUID, reportID int, amount decimal.Decimal, platform string) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertBATLossEvent", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertBATLossEvent(ctx, paymentID, reportID, amount, platform) -} - -// InsertClobberedClaims implements Datastore -func (_d DatastoreWithPrometheus) InsertClobberedClaims(ctx context.Context, ids []uuid.UUID, version int) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertClobberedClaims", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertClobberedClaims(ctx, ids, version) -} - -// InsertIssuer implements Datastore -func (_d DatastoreWithPrometheus) InsertIssuer(issuer *Issuer) (ip1 *Issuer, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertIssuer(issuer) -} - -// InsertSuggestion implements Datastore -func (_d DatastoreWithPrometheus) InsertSuggestion(credentials []cbr.CredentialRedemption, suggestionText string, suggestion []byte) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertSuggestion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertSuggestion(credentials, suggestionText, suggestion) -} - -// Migrate implements Datastore -func (_d DatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements Datastore -func (_d DatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements Datastore -func (_d DatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements Datastore -func (_d DatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements Datastore -func (_d DatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} - -// RunNextClaimJob implements Datastore -func (_d DatastoreWithPrometheus) RunNextClaimJob(ctx context.Context, worker ClaimWorker) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RunNextClaimJob", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RunNextClaimJob(ctx, worker) -} - -// RunNextSuggestionJob implements Datastore -func (_d DatastoreWithPrometheus) RunNextSuggestionJob(ctx context.Context, worker SuggestionWorker) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RunNextSuggestionJob", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RunNextSuggestionJob(ctx, worker) -} - -// SaveClaimCreds implements Datastore -func (_d DatastoreWithPrometheus) SaveClaimCreds(claimCreds *ClaimCreds) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "SaveClaimCreds", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.SaveClaimCreds(claimCreds) -} - -// UpdateOrder implements Datastore -func (_d DatastoreWithPrometheus) UpdateOrder(orderID uuid.UUID, status string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "UpdateOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpdateOrder(orderID, status) -} diff --git a/services/promotion/instrumented_read_only_datastore.go b/services/promotion/instrumented_read_only_datastore.go deleted file mode 100644 index 95fa5ff69..000000000 --- a/services/promotion/instrumented_read_only_datastore.go +++ /dev/null @@ -1,274 +0,0 @@ -package promotion - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/promotion -i ReadOnlyDatastore -t ../../.prom-gowrap.tmpl -o instrumented_read_only_datastore.go -l "" - -import ( - "time" - - walletutils "github.com/brave-intl/bat-go/libs/wallet" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// ReadOnlyDatastoreWithPrometheus implements ReadOnlyDatastore interface with all methods wrapped -// with Prometheus metrics -type ReadOnlyDatastoreWithPrometheus struct { - base ReadOnlyDatastore - instanceName string -} - -var readonlydatastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "promotion_readonly_datastore_duration_seconds", - Help: "readonlydatastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewReadOnlyDatastoreWithPrometheus returns an instance of the ReadOnlyDatastore decorated with prometheus summary metric -func NewReadOnlyDatastoreWithPrometheus(base ReadOnlyDatastore, instanceName string) ReadOnlyDatastoreWithPrometheus { - return ReadOnlyDatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// BeginTx implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// GetAvailablePromotions implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetAvailablePromotions(platform string) (pa1 []Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetAvailablePromotions", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetAvailablePromotions(platform) -} - -// GetAvailablePromotionsForWallet implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetAvailablePromotionsForWallet(wallet *walletutils.Info, platform string) (pa1 []Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetAvailablePromotionsForWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetAvailablePromotionsForWallet(wallet, platform) -} - -// GetClaimByWalletAndPromotion implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetClaimByWalletAndPromotion(wallet *walletutils.Info, promotionID *Promotion) (cp1 *Claim, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetClaimByWalletAndPromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetClaimByWalletAndPromotion(wallet, promotionID) -} - -// GetClaimCreds implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetClaimCreds(claimID uuid.UUID) (cp1 *ClaimCreds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetClaimCreds", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetClaimCreds(claimID) -} - -// GetClaimSummary implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetClaimSummary(walletID uuid.UUID, grantType string) (cp1 *ClaimSummary, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetClaimSummary", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetClaimSummary(walletID, grantType) -} - -// GetIssuer implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetIssuer(promotionID uuid.UUID, cohort string) (ip1 *Issuer, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuer(promotionID, cohort) -} - -// GetIssuerByPublicKey implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetIssuerByPublicKey(publicKey string) (ip1 *Issuer, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuerByPublicKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuerByPublicKey(publicKey) -} - -// GetPreClaim implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetPreClaim(promotionID uuid.UUID, walletID string) (cp1 *Claim, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPreClaim", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPreClaim(promotionID, walletID) -} - -// GetPromotion implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetPromotion(promotionID uuid.UUID) (pp1 *Promotion, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPromotion", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPromotion(promotionID) -} - -// GetPromotionsMissingIssuer implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetPromotionsMissingIssuer(limit int) (ua1 []uuid.UUID, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPromotionsMissingIssuer", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPromotionsMissingIssuer(limit) -} - -// GetWithdrawalsAssociated implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetWithdrawalsAssociated(walletID *uuid.UUID, claimID *uuid.UUID) (up1 *uuid.UUID, d1 decimal.Decimal, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetWithdrawalsAssociated", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetWithdrawalsAssociated(walletID, claimID) -} - -// Migrate implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} diff --git a/services/promotion/issuer.go b/services/promotion/issuer.go deleted file mode 100644 index 3f8a688ea..000000000 --- a/services/promotion/issuer.go +++ /dev/null @@ -1,57 +0,0 @@ -package promotion - -import ( - "context" - - uuid "github.com/satori/go.uuid" -) - -const ( - defaultMaxTokensPerIssuer = 4000000 // ~1M BAT -) - -// Issuer includes information about a particular credential issuer -type Issuer struct { - ID uuid.UUID `db:"id"` - PromotionID uuid.UUID `db:"promotion_id"` - Cohort string - PublicKey string `db:"public_key"` -} - -// CreateIssuer creates a new challenge bypass credential issuer, saving it's information into the datastore -func (service *Service) CreateIssuer(ctx context.Context, promotionID uuid.UUID, cohort string) (*Issuer, error) { - issuer := &Issuer{PromotionID: promotionID, Cohort: cohort, PublicKey: ""} - - err := service.cbClient.CreateIssuer(ctx, issuer.Name(), defaultMaxTokensPerIssuer) - if err != nil { - return nil, err - } - - resp, err := service.cbClient.GetIssuer(ctx, issuer.Name()) - if err != nil { - return nil, err - } - - issuer.PublicKey = resp.PublicKey - - return service.Datastore.InsertIssuer(issuer) -} - -// Name returns the name of the issuer as known by the challenge bypass server -func (issuer *Issuer) Name() string { - return issuer.PromotionID.String() + ":" + issuer.Cohort -} - -// GetOrCreateIssuer gets a matching issuer if one exists and otherwise creates one -func (service *Service) GetOrCreateIssuer(ctx context.Context, promotionID uuid.UUID, cohort string) (*Issuer, error) { - issuer, err := service.Datastore.GetIssuer(promotionID, cohort) - if err != nil { - return nil, err - } - - if issuer == nil { - issuer, err = service.CreateIssuer(ctx, promotionID, cohort) - } - - return issuer, err -} diff --git a/services/promotion/mockclaim.go b/services/promotion/mockclaim.go deleted file mode 100644 index 85c5c7cc0..000000000 --- a/services/promotion/mockclaim.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./promotion/claim.go - -// Package promotion is a generated GoMock package. -package promotion - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - go_uuid "github.com/satori/go.uuid" -) - -// MockClaimWorker is a mock of ClaimWorker interface. -type MockClaimWorker struct { - ctrl *gomock.Controller - recorder *MockClaimWorkerMockRecorder -} - -// MockClaimWorkerMockRecorder is the mock recorder for MockClaimWorker. -type MockClaimWorkerMockRecorder struct { - mock *MockClaimWorker -} - -// NewMockClaimWorker creates a new mock instance. -func NewMockClaimWorker(ctrl *gomock.Controller) *MockClaimWorker { - mock := &MockClaimWorker{ctrl: ctrl} - mock.recorder = &MockClaimWorkerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockClaimWorker) EXPECT() *MockClaimWorkerMockRecorder { - return m.recorder -} - -// SignClaimCreds mocks base method. -func (m *MockClaimWorker) SignClaimCreds(ctx context.Context, claimID go_uuid.UUID, issuer Issuer, blindedCreds []string) (*ClaimCreds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SignClaimCreds", ctx, claimID, issuer, blindedCreds) - ret0, _ := ret[0].(*ClaimCreds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SignClaimCreds indicates an expected call of SignClaimCreds. -func (mr *MockClaimWorkerMockRecorder) SignClaimCreds(ctx, claimID, issuer, blindedCreds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignClaimCreds", reflect.TypeOf((*MockClaimWorker)(nil).SignClaimCreds), ctx, claimID, issuer, blindedCreds) -} diff --git a/services/promotion/mockdatastore.go b/services/promotion/mockdatastore.go deleted file mode 100644 index db81742df..000000000 --- a/services/promotion/mockdatastore.go +++ /dev/null @@ -1,822 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./promotion/datastore.go - -// Package promotion is a generated GoMock package. -package promotion - -import ( - context "context" - reflect "reflect" - - cbr "github.com/brave-intl/bat-go/libs/clients/cbr" - jsonutils "github.com/brave-intl/bat-go/libs/jsonutils" - wallet "github.com/brave-intl/bat-go/libs/wallet" - v4 "github.com/golang-migrate/migrate/v4" - gomock "github.com/golang/mock/gomock" - sqlx "github.com/jmoiron/sqlx" - go_uuid "github.com/satori/go.uuid" - decimal "github.com/shopspring/decimal" -) - -// MockDatastore is a mock of Datastore interface. -type MockDatastore struct { - ctrl *gomock.Controller - recorder *MockDatastoreMockRecorder -} - -// MockDatastoreMockRecorder is the mock recorder for MockDatastore. -type MockDatastoreMockRecorder struct { - mock *MockDatastore -} - -// NewMockDatastore creates a new mock instance. -func NewMockDatastore(ctrl *gomock.Controller) *MockDatastore { - mock := &MockDatastore{ctrl: ctrl} - mock.recorder = &MockDatastoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDatastore) EXPECT() *MockDatastoreMockRecorder { - return m.recorder -} - -// ActivatePromotion mocks base method. -func (m *MockDatastore) ActivatePromotion(promotion *Promotion) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ActivatePromotion", promotion) - ret0, _ := ret[0].(error) - return ret0 -} - -// ActivatePromotion indicates an expected call of ActivatePromotion. -func (mr *MockDatastoreMockRecorder) ActivatePromotion(promotion interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivatePromotion", reflect.TypeOf((*MockDatastore)(nil).ActivatePromotion), promotion) -} - -// BeginTx mocks base method. -func (m *MockDatastore) BeginTx() (*sqlx.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeginTx") - ret0, _ := ret[0].(*sqlx.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BeginTx indicates an expected call of BeginTx. -func (mr *MockDatastoreMockRecorder) BeginTx() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockDatastore)(nil).BeginTx)) -} - -// ClaimForWallet mocks base method. -func (m *MockDatastore) ClaimForWallet(promotion *Promotion, issuer *Issuer, wallet *wallet.Info, blindedCreds jsonutils.JSONStringArray) (*Claim, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ClaimForWallet", promotion, issuer, wallet, blindedCreds) - ret0, _ := ret[0].(*Claim) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ClaimForWallet indicates an expected call of ClaimForWallet. -func (mr *MockDatastoreMockRecorder) ClaimForWallet(promotion, issuer, wallet, blindedCreds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClaimForWallet", reflect.TypeOf((*MockDatastore)(nil).ClaimForWallet), promotion, issuer, wallet, blindedCreds) -} - -// CreateClaim mocks base method. -func (m *MockDatastore) CreateClaim(promotionID go_uuid.UUID, walletID string, value, bonus decimal.Decimal, legacy bool) (*Claim, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateClaim", promotionID, walletID, value, bonus, legacy) - ret0, _ := ret[0].(*Claim) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateClaim indicates an expected call of CreateClaim. -func (mr *MockDatastoreMockRecorder) CreateClaim(promotionID, walletID, value, bonus, legacy interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateClaim", reflect.TypeOf((*MockDatastore)(nil).CreateClaim), promotionID, walletID, value, bonus, legacy) -} - -// CreatePromotion mocks base method. -func (m *MockDatastore) CreatePromotion(promotionType string, numGrants int, value decimal.Decimal, platform string) (*Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreatePromotion", promotionType, numGrants, value, platform) - ret0, _ := ret[0].(*Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreatePromotion indicates an expected call of CreatePromotion. -func (mr *MockDatastoreMockRecorder) CreatePromotion(promotionType, numGrants, value, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePromotion", reflect.TypeOf((*MockDatastore)(nil).CreatePromotion), promotionType, numGrants, value, platform) -} - -// CreateTransaction mocks base method. -func (m *MockDatastore) CreateTransaction(orderID go_uuid.UUID, externalTransactionID, status, currency, kind string, amount decimal.Decimal) (*Transaction, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateTransaction", orderID, externalTransactionID, status, currency, kind, amount) - ret0, _ := ret[0].(*Transaction) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateTransaction indicates an expected call of CreateTransaction. -func (mr *MockDatastoreMockRecorder) CreateTransaction(orderID, externalTransactionID, status, currency, kind, amount interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTransaction", reflect.TypeOf((*MockDatastore)(nil).CreateTransaction), orderID, externalTransactionID, status, currency, kind, amount) -} - -// DeactivatePromotion mocks base method. -func (m *MockDatastore) DeactivatePromotion(promotion *Promotion) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeactivatePromotion", promotion) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeactivatePromotion indicates an expected call of DeactivatePromotion. -func (mr *MockDatastoreMockRecorder) DeactivatePromotion(promotion interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeactivatePromotion", reflect.TypeOf((*MockDatastore)(nil).DeactivatePromotion), promotion) -} - -// GetAvailablePromotions mocks base method. -func (m *MockDatastore) GetAvailablePromotions(platform string) ([]Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAvailablePromotions", platform) - ret0, _ := ret[0].([]Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAvailablePromotions indicates an expected call of GetAvailablePromotions. -func (mr *MockDatastoreMockRecorder) GetAvailablePromotions(platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAvailablePromotions", reflect.TypeOf((*MockDatastore)(nil).GetAvailablePromotions), platform) -} - -// GetAvailablePromotionsForWallet mocks base method. -func (m *MockDatastore) GetAvailablePromotionsForWallet(wallet *wallet.Info, platform string) ([]Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAvailablePromotionsForWallet", wallet, platform) - ret0, _ := ret[0].([]Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAvailablePromotionsForWallet indicates an expected call of GetAvailablePromotionsForWallet. -func (mr *MockDatastoreMockRecorder) GetAvailablePromotionsForWallet(wallet, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAvailablePromotionsForWallet", reflect.TypeOf((*MockDatastore)(nil).GetAvailablePromotionsForWallet), wallet, platform) -} - -// GetClaimByWalletAndPromotion mocks base method. -func (m *MockDatastore) GetClaimByWalletAndPromotion(wallet *wallet.Info, promotionID *Promotion) (*Claim, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClaimByWalletAndPromotion", wallet, promotionID) - ret0, _ := ret[0].(*Claim) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetClaimByWalletAndPromotion indicates an expected call of GetClaimByWalletAndPromotion. -func (mr *MockDatastoreMockRecorder) GetClaimByWalletAndPromotion(wallet, promotionID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaimByWalletAndPromotion", reflect.TypeOf((*MockDatastore)(nil).GetClaimByWalletAndPromotion), wallet, promotionID) -} - -// GetClaimCreds mocks base method. -func (m *MockDatastore) GetClaimCreds(claimID go_uuid.UUID) (*ClaimCreds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClaimCreds", claimID) - ret0, _ := ret[0].(*ClaimCreds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetClaimCreds indicates an expected call of GetClaimCreds. -func (mr *MockDatastoreMockRecorder) GetClaimCreds(claimID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaimCreds", reflect.TypeOf((*MockDatastore)(nil).GetClaimCreds), claimID) -} - -// GetClaimSummary mocks base method. -func (m *MockDatastore) GetClaimSummary(walletID go_uuid.UUID, grantType string) (*ClaimSummary, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClaimSummary", walletID, grantType) - ret0, _ := ret[0].(*ClaimSummary) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetClaimSummary indicates an expected call of GetClaimSummary. -func (mr *MockDatastoreMockRecorder) GetClaimSummary(walletID, grantType interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaimSummary", reflect.TypeOf((*MockDatastore)(nil).GetClaimSummary), walletID, grantType) -} - -// GetIssuer mocks base method. -func (m *MockDatastore) GetIssuer(promotionID go_uuid.UUID, cohort string) (*Issuer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuer", promotionID, cohort) - ret0, _ := ret[0].(*Issuer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuer indicates an expected call of GetIssuer. -func (mr *MockDatastoreMockRecorder) GetIssuer(promotionID, cohort interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuer", reflect.TypeOf((*MockDatastore)(nil).GetIssuer), promotionID, cohort) -} - -// GetIssuerByPublicKey mocks base method. -func (m *MockDatastore) GetIssuerByPublicKey(publicKey string) (*Issuer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuerByPublicKey", publicKey) - ret0, _ := ret[0].(*Issuer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuerByPublicKey indicates an expected call of GetIssuerByPublicKey. -func (mr *MockDatastoreMockRecorder) GetIssuerByPublicKey(publicKey interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuerByPublicKey", reflect.TypeOf((*MockDatastore)(nil).GetIssuerByPublicKey), publicKey) -} - -// GetOrder mocks base method. -func (m *MockDatastore) GetOrder(orderID go_uuid.UUID) (*Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrder", orderID) - ret0, _ := ret[0].(*Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOrder indicates an expected call of GetOrder. -func (mr *MockDatastoreMockRecorder) GetOrder(orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrder", reflect.TypeOf((*MockDatastore)(nil).GetOrder), orderID) -} - -// GetPreClaim mocks base method. -func (m *MockDatastore) GetPreClaim(promotionID go_uuid.UUID, walletID string) (*Claim, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPreClaim", promotionID, walletID) - ret0, _ := ret[0].(*Claim) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPreClaim indicates an expected call of GetPreClaim. -func (mr *MockDatastoreMockRecorder) GetPreClaim(promotionID, walletID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPreClaim", reflect.TypeOf((*MockDatastore)(nil).GetPreClaim), promotionID, walletID) -} - -// GetPromotion mocks base method. -func (m *MockDatastore) GetPromotion(promotionID go_uuid.UUID) (*Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPromotion", promotionID) - ret0, _ := ret[0].(*Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPromotion indicates an expected call of GetPromotion. -func (mr *MockDatastoreMockRecorder) GetPromotion(promotionID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPromotion", reflect.TypeOf((*MockDatastore)(nil).GetPromotion), promotionID) -} - -// GetPromotionsMissingIssuer mocks base method. -func (m *MockDatastore) GetPromotionsMissingIssuer(limit int) ([]go_uuid.UUID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPromotionsMissingIssuer", limit) - ret0, _ := ret[0].([]go_uuid.UUID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPromotionsMissingIssuer indicates an expected call of GetPromotionsMissingIssuer. -func (mr *MockDatastoreMockRecorder) GetPromotionsMissingIssuer(limit interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPromotionsMissingIssuer", reflect.TypeOf((*MockDatastore)(nil).GetPromotionsMissingIssuer), limit) -} - -// GetSumForTransactions mocks base method. -func (m *MockDatastore) GetSumForTransactions(orderID go_uuid.UUID) (decimal.Decimal, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSumForTransactions", orderID) - ret0, _ := ret[0].(decimal.Decimal) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSumForTransactions indicates an expected call of GetSumForTransactions. -func (mr *MockDatastoreMockRecorder) GetSumForTransactions(orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSumForTransactions", reflect.TypeOf((*MockDatastore)(nil).GetSumForTransactions), orderID) -} - -// GetWithdrawalsAssociated mocks base method. -func (m *MockDatastore) GetWithdrawalsAssociated(walletID, claimID *go_uuid.UUID) (*go_uuid.UUID, decimal.Decimal, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetWithdrawalsAssociated", walletID, claimID) - ret0, _ := ret[0].(*go_uuid.UUID) - ret1, _ := ret[1].(decimal.Decimal) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetWithdrawalsAssociated indicates an expected call of GetWithdrawalsAssociated. -func (mr *MockDatastoreMockRecorder) GetWithdrawalsAssociated(walletID, claimID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWithdrawalsAssociated", reflect.TypeOf((*MockDatastore)(nil).GetWithdrawalsAssociated), walletID, claimID) -} - -// InsertBAPReportEvent mocks base method. -func (m *MockDatastore) InsertBAPReportEvent(ctx context.Context, paymentID go_uuid.UUID, amount decimal.Decimal) (*go_uuid.UUID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertBAPReportEvent", ctx, paymentID, amount) - ret0, _ := ret[0].(*go_uuid.UUID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// InsertBAPReportEvent indicates an expected call of InsertBAPReportEvent. -func (mr *MockDatastoreMockRecorder) InsertBAPReportEvent(ctx, paymentID, amount interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBAPReportEvent", reflect.TypeOf((*MockDatastore)(nil).InsertBAPReportEvent), ctx, paymentID, amount) -} - -// InsertBATLossEvent mocks base method. -func (m *MockDatastore) InsertBATLossEvent(ctx context.Context, paymentID go_uuid.UUID, reportID int, amount decimal.Decimal, platform string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertBATLossEvent", ctx, paymentID, reportID, amount, platform) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// InsertBATLossEvent indicates an expected call of InsertBATLossEvent. -func (mr *MockDatastoreMockRecorder) InsertBATLossEvent(ctx, paymentID, reportID, amount, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBATLossEvent", reflect.TypeOf((*MockDatastore)(nil).InsertBATLossEvent), ctx, paymentID, reportID, amount, platform) -} - -// InsertClobberedClaims mocks base method. -func (m *MockDatastore) InsertClobberedClaims(ctx context.Context, ids []go_uuid.UUID, version int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertClobberedClaims", ctx, ids, version) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertClobberedClaims indicates an expected call of InsertClobberedClaims. -func (mr *MockDatastoreMockRecorder) InsertClobberedClaims(ctx, ids, version interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertClobberedClaims", reflect.TypeOf((*MockDatastore)(nil).InsertClobberedClaims), ctx, ids, version) -} - -// InsertIssuer mocks base method. -func (m *MockDatastore) InsertIssuer(issuer *Issuer) (*Issuer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertIssuer", issuer) - ret0, _ := ret[0].(*Issuer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// InsertIssuer indicates an expected call of InsertIssuer. -func (mr *MockDatastoreMockRecorder) InsertIssuer(issuer interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIssuer", reflect.TypeOf((*MockDatastore)(nil).InsertIssuer), issuer) -} - -// InsertSuggestion mocks base method. -func (m *MockDatastore) InsertSuggestion(credentials []cbr.CredentialRedemption, suggestionText string, suggestion []byte) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertSuggestion", credentials, suggestionText, suggestion) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertSuggestion indicates an expected call of InsertSuggestion. -func (mr *MockDatastoreMockRecorder) InsertSuggestion(credentials, suggestionText, suggestion interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertSuggestion", reflect.TypeOf((*MockDatastore)(nil).InsertSuggestion), credentials, suggestionText, suggestion) -} - -// Migrate mocks base method. -func (m *MockDatastore) Migrate(arg0 ...uint) error { - m.ctrl.T.Helper() - varargs := []interface{}{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Migrate", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Migrate indicates an expected call of Migrate. -func (mr *MockDatastoreMockRecorder) Migrate(arg0 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Migrate", reflect.TypeOf((*MockDatastore)(nil).Migrate), arg0...) -} - -// NewMigrate mocks base method. -func (m *MockDatastore) NewMigrate() (*v4.Migrate, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewMigrate") - ret0, _ := ret[0].(*v4.Migrate) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewMigrate indicates an expected call of NewMigrate. -func (mr *MockDatastoreMockRecorder) NewMigrate() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewMigrate", reflect.TypeOf((*MockDatastore)(nil).NewMigrate)) -} - -// RawDB mocks base method. -func (m *MockDatastore) RawDB() *sqlx.DB { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RawDB") - ret0, _ := ret[0].(*sqlx.DB) - return ret0 -} - -// RawDB indicates an expected call of RawDB. -func (mr *MockDatastoreMockRecorder) RawDB() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDB", reflect.TypeOf((*MockDatastore)(nil).RawDB)) -} - -// RollbackTx mocks base method. -func (m *MockDatastore) RollbackTx(tx *sqlx.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RollbackTx", tx) -} - -// RollbackTx indicates an expected call of RollbackTx. -func (mr *MockDatastoreMockRecorder) RollbackTx(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTx", reflect.TypeOf((*MockDatastore)(nil).RollbackTx), tx) -} - -// RollbackTxAndHandle mocks base method. -func (m *MockDatastore) RollbackTxAndHandle(tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RollbackTxAndHandle", tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// RollbackTxAndHandle indicates an expected call of RollbackTxAndHandle. -func (mr *MockDatastoreMockRecorder) RollbackTxAndHandle(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTxAndHandle", reflect.TypeOf((*MockDatastore)(nil).RollbackTxAndHandle), tx) -} - -// RunNextClaimJob mocks base method. -func (m *MockDatastore) RunNextClaimJob(ctx context.Context, worker ClaimWorker) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RunNextClaimJob", ctx, worker) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RunNextClaimJob indicates an expected call of RunNextClaimJob. -func (mr *MockDatastoreMockRecorder) RunNextClaimJob(ctx, worker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunNextClaimJob", reflect.TypeOf((*MockDatastore)(nil).RunNextClaimJob), ctx, worker) -} - -// RunNextSuggestionJob mocks base method. -func (m *MockDatastore) RunNextSuggestionJob(ctx context.Context, worker SuggestionWorker) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RunNextSuggestionJob", ctx, worker) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RunNextSuggestionJob indicates an expected call of RunNextSuggestionJob. -func (mr *MockDatastoreMockRecorder) RunNextSuggestionJob(ctx, worker interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunNextSuggestionJob", reflect.TypeOf((*MockDatastore)(nil).RunNextSuggestionJob), ctx, worker) -} - -// SaveClaimCreds mocks base method. -func (m *MockDatastore) SaveClaimCreds(claimCreds *ClaimCreds) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveClaimCreds", claimCreds) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveClaimCreds indicates an expected call of SaveClaimCreds. -func (mr *MockDatastoreMockRecorder) SaveClaimCreds(claimCreds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveClaimCreds", reflect.TypeOf((*MockDatastore)(nil).SaveClaimCreds), claimCreds) -} - -// UpdateOrder mocks base method. -func (m *MockDatastore) UpdateOrder(orderID go_uuid.UUID, status string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateOrder", orderID, status) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateOrder indicates an expected call of UpdateOrder. -func (mr *MockDatastoreMockRecorder) UpdateOrder(orderID, status interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOrder", reflect.TypeOf((*MockDatastore)(nil).UpdateOrder), orderID, status) -} - -// MockReadOnlyDatastore is a mock of ReadOnlyDatastore interface. -type MockReadOnlyDatastore struct { - ctrl *gomock.Controller - recorder *MockReadOnlyDatastoreMockRecorder -} - -// MockReadOnlyDatastoreMockRecorder is the mock recorder for MockReadOnlyDatastore. -type MockReadOnlyDatastoreMockRecorder struct { - mock *MockReadOnlyDatastore -} - -// NewMockReadOnlyDatastore creates a new mock instance. -func NewMockReadOnlyDatastore(ctrl *gomock.Controller) *MockReadOnlyDatastore { - mock := &MockReadOnlyDatastore{ctrl: ctrl} - mock.recorder = &MockReadOnlyDatastoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockReadOnlyDatastore) EXPECT() *MockReadOnlyDatastoreMockRecorder { - return m.recorder -} - -// BeginTx mocks base method. -func (m *MockReadOnlyDatastore) BeginTx() (*sqlx.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeginTx") - ret0, _ := ret[0].(*sqlx.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BeginTx indicates an expected call of BeginTx. -func (mr *MockReadOnlyDatastoreMockRecorder) BeginTx() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockReadOnlyDatastore)(nil).BeginTx)) -} - -// GetAvailablePromotions mocks base method. -func (m *MockReadOnlyDatastore) GetAvailablePromotions(platform string) ([]Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAvailablePromotions", platform) - ret0, _ := ret[0].([]Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAvailablePromotions indicates an expected call of GetAvailablePromotions. -func (mr *MockReadOnlyDatastoreMockRecorder) GetAvailablePromotions(platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAvailablePromotions", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetAvailablePromotions), platform) -} - -// GetAvailablePromotionsForWallet mocks base method. -func (m *MockReadOnlyDatastore) GetAvailablePromotionsForWallet(wallet *wallet.Info, platform string) ([]Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAvailablePromotionsForWallet", wallet, platform) - ret0, _ := ret[0].([]Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAvailablePromotionsForWallet indicates an expected call of GetAvailablePromotionsForWallet. -func (mr *MockReadOnlyDatastoreMockRecorder) GetAvailablePromotionsForWallet(wallet, platform interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAvailablePromotionsForWallet", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetAvailablePromotionsForWallet), wallet, platform) -} - -// GetClaimByWalletAndPromotion mocks base method. -func (m *MockReadOnlyDatastore) GetClaimByWalletAndPromotion(wallet *wallet.Info, promotionID *Promotion) (*Claim, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClaimByWalletAndPromotion", wallet, promotionID) - ret0, _ := ret[0].(*Claim) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetClaimByWalletAndPromotion indicates an expected call of GetClaimByWalletAndPromotion. -func (mr *MockReadOnlyDatastoreMockRecorder) GetClaimByWalletAndPromotion(wallet, promotionID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaimByWalletAndPromotion", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetClaimByWalletAndPromotion), wallet, promotionID) -} - -// GetClaimCreds mocks base method. -func (m *MockReadOnlyDatastore) GetClaimCreds(claimID go_uuid.UUID) (*ClaimCreds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClaimCreds", claimID) - ret0, _ := ret[0].(*ClaimCreds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetClaimCreds indicates an expected call of GetClaimCreds. -func (mr *MockReadOnlyDatastoreMockRecorder) GetClaimCreds(claimID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaimCreds", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetClaimCreds), claimID) -} - -// GetClaimSummary mocks base method. -func (m *MockReadOnlyDatastore) GetClaimSummary(walletID go_uuid.UUID, grantType string) (*ClaimSummary, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetClaimSummary", walletID, grantType) - ret0, _ := ret[0].(*ClaimSummary) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetClaimSummary indicates an expected call of GetClaimSummary. -func (mr *MockReadOnlyDatastoreMockRecorder) GetClaimSummary(walletID, grantType interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaimSummary", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetClaimSummary), walletID, grantType) -} - -// GetIssuer mocks base method. -func (m *MockReadOnlyDatastore) GetIssuer(promotionID go_uuid.UUID, cohort string) (*Issuer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuer", promotionID, cohort) - ret0, _ := ret[0].(*Issuer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuer indicates an expected call of GetIssuer. -func (mr *MockReadOnlyDatastoreMockRecorder) GetIssuer(promotionID, cohort interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuer", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetIssuer), promotionID, cohort) -} - -// GetIssuerByPublicKey mocks base method. -func (m *MockReadOnlyDatastore) GetIssuerByPublicKey(publicKey string) (*Issuer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuerByPublicKey", publicKey) - ret0, _ := ret[0].(*Issuer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuerByPublicKey indicates an expected call of GetIssuerByPublicKey. -func (mr *MockReadOnlyDatastoreMockRecorder) GetIssuerByPublicKey(publicKey interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuerByPublicKey", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetIssuerByPublicKey), publicKey) -} - -// GetPreClaim mocks base method. -func (m *MockReadOnlyDatastore) GetPreClaim(promotionID go_uuid.UUID, walletID string) (*Claim, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPreClaim", promotionID, walletID) - ret0, _ := ret[0].(*Claim) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPreClaim indicates an expected call of GetPreClaim. -func (mr *MockReadOnlyDatastoreMockRecorder) GetPreClaim(promotionID, walletID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPreClaim", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetPreClaim), promotionID, walletID) -} - -// GetPromotion mocks base method. -func (m *MockReadOnlyDatastore) GetPromotion(promotionID go_uuid.UUID) (*Promotion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPromotion", promotionID) - ret0, _ := ret[0].(*Promotion) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPromotion indicates an expected call of GetPromotion. -func (mr *MockReadOnlyDatastoreMockRecorder) GetPromotion(promotionID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPromotion", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetPromotion), promotionID) -} - -// GetPromotionsMissingIssuer mocks base method. -func (m *MockReadOnlyDatastore) GetPromotionsMissingIssuer(limit int) ([]go_uuid.UUID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPromotionsMissingIssuer", limit) - ret0, _ := ret[0].([]go_uuid.UUID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPromotionsMissingIssuer indicates an expected call of GetPromotionsMissingIssuer. -func (mr *MockReadOnlyDatastoreMockRecorder) GetPromotionsMissingIssuer(limit interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPromotionsMissingIssuer", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetPromotionsMissingIssuer), limit) -} - -// GetWithdrawalsAssociated mocks base method. -func (m *MockReadOnlyDatastore) GetWithdrawalsAssociated(walletID, claimID *go_uuid.UUID) (*go_uuid.UUID, decimal.Decimal, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetWithdrawalsAssociated", walletID, claimID) - ret0, _ := ret[0].(*go_uuid.UUID) - ret1, _ := ret[1].(decimal.Decimal) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetWithdrawalsAssociated indicates an expected call of GetWithdrawalsAssociated. -func (mr *MockReadOnlyDatastoreMockRecorder) GetWithdrawalsAssociated(walletID, claimID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWithdrawalsAssociated", reflect.TypeOf((*MockReadOnlyDatastore)(nil).GetWithdrawalsAssociated), walletID, claimID) -} - -// Migrate mocks base method. -func (m *MockReadOnlyDatastore) Migrate(arg0 ...uint) error { - m.ctrl.T.Helper() - varargs := []interface{}{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Migrate", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Migrate indicates an expected call of Migrate. -func (mr *MockReadOnlyDatastoreMockRecorder) Migrate(arg0 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Migrate", reflect.TypeOf((*MockReadOnlyDatastore)(nil).Migrate), arg0...) -} - -// NewMigrate mocks base method. -func (m *MockReadOnlyDatastore) NewMigrate() (*v4.Migrate, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewMigrate") - ret0, _ := ret[0].(*v4.Migrate) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewMigrate indicates an expected call of NewMigrate. -func (mr *MockReadOnlyDatastoreMockRecorder) NewMigrate() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewMigrate", reflect.TypeOf((*MockReadOnlyDatastore)(nil).NewMigrate)) -} - -// RawDB mocks base method. -func (m *MockReadOnlyDatastore) RawDB() *sqlx.DB { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RawDB") - ret0, _ := ret[0].(*sqlx.DB) - return ret0 -} - -// RawDB indicates an expected call of RawDB. -func (mr *MockReadOnlyDatastoreMockRecorder) RawDB() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDB", reflect.TypeOf((*MockReadOnlyDatastore)(nil).RawDB)) -} - -// RollbackTx mocks base method. -func (m *MockReadOnlyDatastore) RollbackTx(tx *sqlx.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RollbackTx", tx) -} - -// RollbackTx indicates an expected call of RollbackTx. -func (mr *MockReadOnlyDatastoreMockRecorder) RollbackTx(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTx", reflect.TypeOf((*MockReadOnlyDatastore)(nil).RollbackTx), tx) -} - -// RollbackTxAndHandle mocks base method. -func (m *MockReadOnlyDatastore) RollbackTxAndHandle(tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RollbackTxAndHandle", tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// RollbackTxAndHandle indicates an expected call of RollbackTxAndHandle. -func (mr *MockReadOnlyDatastoreMockRecorder) RollbackTxAndHandle(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTxAndHandle", reflect.TypeOf((*MockReadOnlyDatastore)(nil).RollbackTxAndHandle), tx) -} diff --git a/services/promotion/mockdrain.go b/services/promotion/mockdrain.go deleted file mode 100644 index 156c80ce0..000000000 --- a/services/promotion/mockdrain.go +++ /dev/null @@ -1,5 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./promotion/drain.go - -// Package promotion is a generated GoMock package. -package promotion diff --git a/services/promotion/mockservice.go b/services/promotion/mockservice.go deleted file mode 100644 index 28e4409ce..000000000 --- a/services/promotion/mockservice.go +++ /dev/null @@ -1,5 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./promotion/service.go - -// Package promotion is a generated GoMock package. -package promotion diff --git a/services/promotion/order.go b/services/promotion/order.go deleted file mode 100644 index 0c464d63c..000000000 --- a/services/promotion/order.go +++ /dev/null @@ -1,72 +0,0 @@ -package promotion - -import ( - "time" - - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - - "github.com/brave-intl/bat-go/libs/datastore" -) - -// Delete this file once the issue is completed -// https://github.com/brave-intl/bat-go/issues/263 - -// Order includes information about a particular order -type Order struct { - ID uuid.UUID `json:"id" db:"id"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - Currency string `json:"currency" db:"currency"` - UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` - TotalPrice decimal.Decimal `json:"totalPrice" db:"total_price"` - MerchantID string `json:"merchantId" db:"merchant_id"` - Location datastore.NullString `json:"location" db:"location"` - Status string `json:"status" db:"status"` - Items []OrderItem `json:"items"` - AllowedPaymentMethods pq.StringArray `json:"allowedPaymentMethods" db:"allowed_payment_methods"` - Metadata datastore.Metadata `json:"metadata" db:"metadata"` - LastPaidAt *time.Time `json:"lastPaidAt" db:"last_paid_at"` - ExpiresAt *time.Time `json:"expiresAt" db:"expires_at"` - ValidFor *time.Duration `json:"validFor" db:"valid_for"` - TrialDays *int64 `json:"-" db:"trial_days"` -} - -// OrderItem includes information about a particular order item -type OrderItem struct { - ID uuid.UUID `json:"id" db:"id"` - OrderID uuid.UUID `json:"orderId" db:"order_id"` - SKU string `json:"sku" db:"sku"` - CreatedAt *time.Time `json:"createdAt" db:"created_at"` - UpdatedAt *time.Time `json:"updatedAt" db:"updated_at"` - Currency string `json:"currency" db:"currency"` - Quantity int `json:"quantity" db:"quantity"` - Price decimal.Decimal `json:"price" db:"price"` - Subtotal decimal.Decimal `json:"subtotal"` - Location datastore.NullString `json:"location" db:"location"` - Description datastore.NullString `json:"description" db:"description"` - CredentialType string `json:"credentialType" db:"credential_type"` - ValidFor *time.Duration `json:"validFor" db:"valid_for"` - ValidForISO *string `json:"validForIso" db:"valid_for_iso"` - Metadata datastore.Metadata `json:"metadata" db:"metadata"` - IssuanceIntervalISO *string `json:"issuanceInterval" db:"issuance_interval"` - EachCredentialValidForISO *string `json:"-" db:"each_credential_valid_for_iso"` -} - -// IsPaid returns true if the order is paid -func (order Order) IsPaid() bool { - return order.Status == "paid" -} - -// Transaction includes information about a particular order. Status can be pending, failure, completed, or error. -type Transaction struct { - ID uuid.UUID `json:"id" db:"id"` - OrderID uuid.UUID `json:"order_id" db:"order_id"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` - ExternalTransactionID string `json:"external_transaction_id" db:"external_transaction_id"` - Status string `json:"status" db:"status"` - Currency string `json:"currency" db:"currency"` - Kind string `json:"kind" db:"kind"` - Amount decimal.Decimal `json:"amount" db:"amount"` -} diff --git a/services/promotion/promotion.go b/services/promotion/promotion.go deleted file mode 100644 index e2825949c..000000000 --- a/services/promotion/promotion.go +++ /dev/null @@ -1,145 +0,0 @@ -package promotion - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/jsonutils" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/prometheus/client_golang/prometheus" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -var ( - defaultVoteValue = decimal.NewFromFloat(0.25) - promotionGetCount = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "promotion_get_count", - Help: "a count of the number of times the promotions were collected", - }, - []string{"filter", "migrate"}, - ) - promotionExposureCount = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "promotion_exposure_count", - Help: "a count of the number of times a single promotion was exposed to clients", - }, - []string{"id"}, - ) -) - -func init() { - // register our metrics with prometheus - if err := prometheus.Register(promotionGetCount); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - promotionGetCount = ae.ExistingCollector.(*prometheus.CounterVec) - } - } - - if err := prometheus.Register(promotionExposureCount); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - promotionExposureCount = ae.ExistingCollector.(*prometheus.CounterVec) - } - } -} - -// Promotion includes information about a particular promotion -type Promotion struct { - ID uuid.UUID `json:"id" db:"id"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - ExpiresAt time.Time `json:"expiresAt" db:"expires_at"` - Version int `json:"version" db:"version"` - SuggestionsPerGrant int `json:"suggestionsPerGrant" db:"suggestions_per_grant"` - ApproximateValue decimal.Decimal `json:"approximateValue" db:"approximate_value"` - Type string `json:"type" db:"promotion_type"` - RemainingGrants int `json:"-" db:"remaining_grants"` - Active bool `json:"-" db:"active"` - Available bool `json:"available" db:"available"` - Platform string `json:"platform" db:"platform"` - PublicKeys jsonutils.JSONStringArray `json:"publicKeys" db:"public_keys"` - // warning, legacy claimed is not defined in promotions, but rather as a claim attribute - LegacyClaimed bool `json:"legacyClaimed" db:"legacy_claimed"` - ClaimableUntil time.Time `json:"claimableUntil" db:"claimable_until"` - ClaimableUntilOverride *time.Time `json:"-" db:"claimable_until_override"` -} - -// Filter promotions to all that satisfy the function passed -func Filter(orig []Promotion, f func(Promotion) bool) []Promotion { - promos := make([]Promotion, 0) - for _, p := range orig { - if f(p) { - promos = append(promos, p) - } - } - return promos -} - -// CredentialValue returns the approximate value of a credential -func (promotion *Promotion) CredentialValue() decimal.Decimal { - cv := promotion.ApproximateValue.Div(decimal.New(int64(promotion.SuggestionsPerGrant), 0)) - if !cv.Sub(decimal.NewFromFloat(0.25)).IsZero() { - panic("wrong credential value calculated for promotion") - } - return cv -} - -// Claimable checks whether the promotion can be claimed -func (promotion *Promotion) Claimable(overrideAutoExpiry bool) bool { - // always refuse expired promotions - if promotion.Expired() { - return false - } - // override auto expiry (in legacy claimed case as example) - if overrideAutoExpiry { - return true - } - // expire grants created 3 months ago - if promotion.CreatedAt.Before(time.Now().AddDate(0, -3, 0)) { - return false - } - return true -} - -// Expired check if now is after the expires_at time -func (promotion *Promotion) Expired() bool { - return promotion.ExpiresAt.Before(time.Now()) -} - -// GetAvailablePromotions first tries to look up the wallet and then retrieves available promotions -func (service *Service) GetAvailablePromotions( - ctx context.Context, - walletID *uuid.UUID, - platform string, - migrate bool, -) (*[]Promotion, error) { - if walletID != nil { - logging.AddWalletIDToContext(ctx, *walletID) - - wallet, err := service.wallet.GetWallet(ctx, *walletID) - if err != nil { - return nil, err - } - if wallet == nil { - return nil, nil - } - - promos, err := service.ReadableDatastore().GetAvailablePromotionsForWallet(wallet, platform) - if err != nil { - return nil, err - } - - // Quick hack FIXME - for i := 0; i < len(promos); i++ { - promos[i].ApproximateValue = decimal.New(int64(promos[i].SuggestionsPerGrant), 0).Mul(defaultVoteValue) - } - - if !migrate { - promos = Filter(promos, func(p Promotion) bool { return !p.LegacyClaimed }) - } - - return &promos, nil - } - promos, err := service.ReadableDatastore().GetAvailablePromotions(platform) - return &promos, err -} diff --git a/services/promotion/promotion_test.go b/services/promotion/promotion_test.go deleted file mode 100644 index d73a8285e..000000000 --- a/services/promotion/promotion_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package promotion - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" -) - -type PromotionTestSuite struct { - suite.Suite -} - -func (suite *PromotionTestSuite) SetupSuite() { -} - -func (suite *PromotionTestSuite) SetupTest() { -} - -func (suite *PromotionTestSuite) TearDownTest() { -} - -func TestPromotionTestSuite(t *testing.T) { - suite.Run(t, new(PromotionTestSuite)) -} - -func (suite *PromotionTestSuite) TestPromotionExpired() { - p := Promotion{ - ExpiresAt: time.Now().Add(-1 * time.Second), - } - suite.Require().True(p.Expired()) - p.ExpiresAt = p.ExpiresAt.AddDate(0, 0, 1) - suite.Require().False(p.Expired()) -} - -type Assertion struct { - Claimable bool - LegacyClaimed bool - Promotion Promotion -} - -func (suite *PromotionTestSuite) TestPromotionClaimable() { - now := time.Now() - monthsAgo3 := now.AddDate(0, -3, 0) - scenarios := []Assertion{{ - Claimable: true, // we no longer do Gone if the promotion active flag is false - LegacyClaimed: false, - Promotion: Promotion{ - Active: false, // fails because not active - CreatedAt: monthsAgo3.Add(time.Minute), - ExpiresAt: now.Add(time.Minute), - }, - }, { - Claimable: true, - LegacyClaimed: false, - Promotion: Promotion{ - Active: true, - CreatedAt: monthsAgo3.Add(time.Minute), - ExpiresAt: now.Add(time.Minute), - }, - }, { - Claimable: true, - LegacyClaimed: true, - Promotion: Promotion{ - Active: true, - CreatedAt: monthsAgo3.Add(time.Minute), - ExpiresAt: now.Add(time.Minute), - }, - }, { - Claimable: false, - LegacyClaimed: true, - Promotion: Promotion{ - Active: true, - CreatedAt: monthsAgo3, - ExpiresAt: now, - }, - }, { - Claimable: true, - LegacyClaimed: false, - Promotion: Promotion{ - Active: true, - CreatedAt: monthsAgo3.Add(time.Minute), - ExpiresAt: now.Add(time.Minute), - }, - }} - for _, s := range scenarios { - suite.Require().Equal(s.Claimable, s.Promotion.Claimable(s.LegacyClaimed)) - } -} diff --git a/services/promotion/service.go b/services/promotion/service.go deleted file mode 100644 index f69213b9b..000000000 --- a/services/promotion/service.go +++ /dev/null @@ -1,285 +0,0 @@ -package promotion - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "os" - "sync" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients/cbr" - "github.com/brave-intl/bat-go/libs/clients/reputation" - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/httpsignature" - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - "github.com/brave-intl/bat-go/libs/logging" - srv "github.com/brave-intl/bat-go/libs/service" - w "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/brave-intl/bat-go/services/wallet" - "github.com/linkedin/goavro" - "github.com/prometheus/client_golang/prometheus" - kafka "github.com/segmentio/kafka-go" - "golang.org/x/crypto/ed25519" -) - -const localEnv = "local" - -var ( - suggestionTopic = os.Getenv("ENV") + ".grant.suggestion" - adminAttestationTopic = fmt.Sprintf("admin_attestation_events.%s.repsys.upstream", os.Getenv("ENV")) - - // countContributionsTotal counts the number of contributions made broken down by funding and type - countContributionsTotal = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "contributions_total", - Help: "count of contributions made ( since last start ) broken down by funding and type", - }, - []string{"funding", "type"}, - ) - - // countContributionsBatTotal counts the total value of contributions in terms of bat ( since last start ) broken down by funding and type - countContributionsBatTotal = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "contributions_bat_total", - Help: "total value of contributions in terms of bat ( since last start ) broken down by funding and type", - }, - []string{"funding", "type"}, - ) - - // countGrantsClaimedTotal counts the grants claimed, broken down by platform and type - countGrantsClaimedTotal = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "grants_claimed_total", - Help: "count of grants claimed ( since last start ) broken down by platform and type", - }, - []string{"platform", "type", "legacy"}, - ) - - // countGrantsClaimedBatTotal counts the total value of grants claimed in terms of bat ( since last start ) broken down by platform and type - countGrantsClaimedBatTotal = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "grants_claimed_bat_total", - Help: "total value of grants claimed in terms of bat ( since last start ) broken down by platform and type", - }, - []string{"platform", "type", "legacy"}, - ) -) - -// SetSuggestionTopic allows for a new topic to be suggested -func SetSuggestionTopic(newTopic string) { - suggestionTopic = newTopic -} - -// SetAdminAttestationTopic set admin attestation topic -func SetAdminAttestationTopic(newTopic string) { - adminAttestationTopic = newTopic -} - -// Service contains datastore and challenge bypass client connections -type Service struct { - wallet *wallet.Service - Datastore Datastore - RoDatastore ReadOnlyDatastore - cbClient cbr.Client - reputationClient reputation.Client - codecs map[string]*goavro.Codec - kafkaWriter *kafka.Writer - kafkaDialer *kafka.Dialer - kafkaAdminAttestationReader kafkautils.Consumer - hotWallet *uphold.Wallet - jobs []srv.Job - pauseSuggestionsUntil time.Time - pauseSuggestionsUntilMu sync.RWMutex -} - -// Jobs - Implement srv.JobService interface -func (service *Service) Jobs() []srv.Job { - return service.jobs -} - -// InitKafka by creating a kafka writer and creating local copies of codecs -func (service *Service) InitKafka(ctx context.Context) error { - - // TODO: eventually as cobra/viper - ctx = context.WithValue(ctx, appctx.KafkaBrokersCTXKey, os.Getenv("KAFKA_BROKERS")) - - var err error - service.kafkaWriter, service.kafkaDialer, err = kafkautils.InitKafkaWriter(ctx, suggestionTopic) - if err != nil { - return fmt.Errorf("failed to initialize kafka: %w", err) - } - - service.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "suggestion": suggestionEventSchema, - adminAttestationTopic: adminAttestationEventSchema, - }) - - if err != nil { - return fmt.Errorf("failed to generate codecs kafka: %w", err) - } - return nil -} - -// InitHotWallet by reading the keypair and card id from the environment -func (service *Service) InitHotWallet(ctx context.Context) error { - grantWalletPublicKeyHex := os.Getenv("GRANT_WALLET_PUBLIC_KEY") - grantWalletPrivateKeyHex := os.Getenv("GRANT_WALLET_PRIVATE_KEY") - grantWalletCardID := os.Getenv("GRANT_WALLET_CARD_ID") - - if len(grantWalletCardID) > 0 { - var info w.Info - info.Provider = "uphold" - info.ProviderID = grantWalletCardID - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - var pubKey httpsignature.Ed25519PubKey - var privKey ed25519.PrivateKey - var err error - - pubKey, err = hex.DecodeString(grantWalletPublicKeyHex) - if err != nil { - return errorutils.Wrap(err, "grantWalletPublicKeyHex is invalid") - } - privKey, err = hex.DecodeString(grantWalletPrivateKeyHex) - if err != nil { - return errorutils.Wrap(err, "grantWalletPrivateKeyHex is invalid") - } - - service.hotWallet, err = uphold.New(ctx, info, privKey, pubKey) - if err != nil { - return err - } - } else if os.Getenv("ENV") != localEnv { - return errors.New("GRANT_WALLET_CARD_ID must be set in production") - } - return nil -} - -// InitService creates a service using the passed datastore and clients configured from the environment -func InitService( - ctx context.Context, - promotionDB Datastore, - promotionRODB ReadOnlyDatastore, - walletService *wallet.Service, -) (*Service, error) { - - // register metrics with prometheus - if err := prometheus.Register(countGrantsClaimedBatTotal); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - countGrantsClaimedBatTotal = ae.ExistingCollector.(*prometheus.CounterVec) - } - } - if err := prometheus.Register(countGrantsClaimedTotal); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - countGrantsClaimedTotal = ae.ExistingCollector.(*prometheus.CounterVec) - } - } - - if err := prometheus.Register(countContributionsBatTotal); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - countContributionsBatTotal = ae.ExistingCollector.(*prometheus.CounterVec) - } - } - - if err := prometheus.Register(countContributionsTotal); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - countContributionsTotal = ae.ExistingCollector.(*prometheus.CounterVec) - } - } - - cbClient, err := cbr.New() - if err != nil { - return nil, err - } - - reputationClient, err := reputation.New() - // okay to fail to make a reputation client if the environment is local - if err != nil && os.Getenv("ENV") != localEnv { - return nil, err - } - - service := &Service{ - Datastore: promotionDB, - RoDatastore: promotionRODB, - cbClient: cbClient, - reputationClient: reputationClient, - wallet: walletService, - pauseSuggestionsUntilMu: sync.RWMutex{}, - } - - // setup runnable jobs - service.jobs = []srv.Job{ - { - Func: service.RunNextPromotionMissingIssuer, - Cadence: 5 * time.Second, - Workers: 1, - }, - { - Func: service.RunNextClaimJob, - Cadence: 5 * time.Second, - Workers: 1, - }, - { - Func: service.RunNextSuggestionJob, - Cadence: 5 * time.Second, - Workers: 1, - }, - } - - err = service.InitKafka(ctx) - if err != nil { - return nil, err - } - - err = service.InitHotWallet(ctx) - if err != nil { - return nil, err - } - return service, nil -} - -// ReadableDatastore returns a read only datastore if available, otherwise a normal datastore -func (service *Service) ReadableDatastore() ReadOnlyDatastore { - if service.RoDatastore != nil { - return service.RoDatastore - } - return service.Datastore -} - -// RunNextClaimJob takes the next claim job and completes it -func (service *Service) RunNextClaimJob(ctx context.Context) (bool, error) { - return service.Datastore.RunNextClaimJob(ctx, service) -} - -// RunNextSuggestionJob takes the next suggestion job and completes it -func (service *Service) RunNextSuggestionJob(ctx context.Context) (bool, error) { - return service.Datastore.RunNextSuggestionJob(ctx, service) -} - -// RunNextPromotionMissingIssuer takes the next job and completes it -func (service *Service) RunNextPromotionMissingIssuer(ctx context.Context) (bool, error) { - // get logger from context - logger := logging.Logger(ctx, "wallet.RunNextPromotionMissingIssuer") - - // create issuer for all of the promotions without an issuer - uuids, err := service.RoDatastore.GetPromotionsMissingIssuer(100) - if err != nil { - logger.Error().Err(err).Msg("failed to get promotions from datastore") - return false, fmt.Errorf("failed to get promotions from datastore: %w", err) - } - - for _, uuid := range uuids { - if _, err := service.CreateIssuer(ctx, uuid, "control"); err != nil { - logger.Error().Err(err).Msg("failed to create issuer") - } - } - return true, nil -} diff --git a/services/promotion/service_test.go b/services/promotion/service_test.go deleted file mode 100644 index 1d1d9200b..000000000 --- a/services/promotion/service_test.go +++ /dev/null @@ -1,109 +0,0 @@ -//go:build integration - -package promotion - -import ( - "context" - "testing" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/test" - "github.com/brave-intl/bat-go/services/wallet" - - // re-using viper bind-env for wallet env variables - _ "github.com/brave-intl/bat-go/services/wallet/cmd" - "github.com/stretchr/testify/suite" -) - -type ServiceTestSuite struct { - suite.Suite -} - -func (suite *ServiceTestSuite) SetupSuite() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - m, err := pg.NewMigrate() - suite.Require().NoError(err, "Failed to create migrate instance") - - ver, dirty, _ := m.Version() - if dirty { - suite.Require().NoError(m.Force(int(ver))) - } - if ver > 0 { - suite.Require().NoError(m.Down(), "Failed to migrate down cleanly") - } - - suite.Require().NoError(pg.Migrate(), "Failed to fully migrate") -} - -func (suite *ServiceTestSuite) SetupTest() { - suite.CleanDB() -} - -func (suite *ServiceTestSuite) TearDownTest() { - suite.CleanDB() -} - -func (suite *ServiceTestSuite) CleanDB() { - tables := []string{"claim_creds", "claims", "wallets", "issuers", "promotions"} - - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - for _, table := range tables { - _, err = pg.RawDB().Exec("delete from " + table) - suite.Require().NoError(err, "Failed to get clean table") - } -} - -func TestServiceTestSuite(t *testing.T) { - suite.Run(t, new(ServiceTestSuite)) -} - -func (suite *ServiceTestSuite) createService() (*Service, context.Context) { - ctx := context.Background() - ctx = context.WithValue(ctx, appctx.ParametersMergeBucketCTXKey, test.RandomString()) - ctx = context.WithValue(ctx, appctx.DisabledWalletGeoCountriesCTXKey, test.RandomString()) - - db, _, err := wallet.NewPostgres() - suite.Require().NoError(err) - walletService := &wallet.Service{Datastore: db} - promotionDB, promotionRODB, err := NewPostgres() - suite.Require().NoError(err, "unable connect to promotion db") - s, err := InitService( - ctx, - promotionDB, - promotionRODB, - walletService, - ) - suite.Require().NoError(err) - - return s, ctx -} - -func (suite *ServiceTestSuite) TestGetAvailablePromotions() { - var nilPromotions *[]Promotion - noPromotions := []Promotion{} - service, ctx := suite.createService() - - walletID := new(inputs.ID) - id := walletID.UUID() - - promotions, err := service.GetAvailablePromotions(ctx, id, "", true) - suite.Require().NoError(err) - suite.Require().Equal(&noPromotions, promotions) - - err = inputs.DecodeAndValidateString( - ctx, - walletID, - "00000000-0000-0000-0000-000000000000", - ) - suite.Require().NoError(err) - - id = walletID.UUID() - promotions, err = service.GetAvailablePromotions(ctx, id, "", true) - suite.Require().NoError(err) - suite.Require().Equal(nilPromotions, promotions) -} diff --git a/services/promotion/suggestions.go b/services/promotion/suggestions.go deleted file mode 100644 index 258f66e0c..000000000 --- a/services/promotion/suggestions.go +++ /dev/null @@ -1,425 +0,0 @@ -package promotion - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "time" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/clients/cbr" - appctx "github.com/brave-intl/bat-go/libs/context" - contextutil "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/getsentry/sentry-go" - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog/log" - uuid "github.com/satori/go.uuid" - kafka "github.com/segmentio/kafka-go" - "github.com/shopspring/decimal" -) - -// FIXME temporary until event producer is hooked up -var enableSuggestionJob = true - -// CredentialBinding includes info needed to redeem a single credential -type CredentialBinding struct { - PublicKey string `json:"publicKey" valid:"base64"` - TokenPreimage string `json:"t" valid:"base64"` - Signature string `json:"signature" valid:"base64"` -} - -// DeduplicateCredentialBindings - given a list of tokens return a deduplicated list -func DeduplicateCredentialBindings(tokens ...CredentialBinding) []CredentialBinding { - var ( - seen = map[string]bool{} - result = []CredentialBinding{} - ) - for _, t := range tokens { - if !seen[t.TokenPreimage] { - seen[t.TokenPreimage] = true - result = append(result, t) - } - } - return result -} - -// Suggestion encapsulates information from the user about where /how they want to contribute -type Suggestion struct { - Type string `json:"type" valid:"in(auto-contribute|oneoff-tip|recurring-tip|payment)"` - Channel string `json:"channel" valid:"-"` - OrderID *uuid.UUID `json:"orderId,omitempty" valid:"-"` -} - -// Base64Decode unmarshalls the suggestion from a string. -func (s *Suggestion) Base64Decode(text string) error { - var bytes []byte - bytes, err := base64.StdEncoding.DecodeString(text) - if err != nil { - return err - } - - err = json.Unmarshal(bytes, s) - return err -} - -/* -{ - "type": "auto-contribute", - "channel": "coinmarketcap.com", - "totalAmount": "15.0", - "funding": [ - { - "type": "ugp", - "amount": "15.0", - "cohort": "control", - "promotion": "{{promotionId}}" - } - ] -} -*/ - -// FundingSource describes where funds for this suggestion should come from -type FundingSource struct { - Type string `json:"type"` - Amount decimal.Decimal `json:"amount"` - Cohort string `json:"cohort"` - PromotionID uuid.UUID `json:"promotion"` - Credentials []cbr.CredentialRedemption `json:"-"` -} - -// SuggestionEvent encapsulates user and server provided information about a request to contribute -type SuggestionEvent struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"createdAt"` - Suggestion - TotalAmount decimal.Decimal `json:"totalAmount"` - Funding []FundingSource `json:"funding"` -} - -// TryUpgradeSuggestionEvent from JSON format to Avro, filling in any potentially missing fields -func (service *Service) TryUpgradeSuggestionEvent(suggestion []byte) ([]byte, error) { - var event SuggestionEvent - - if suggestion[0] == '{' { - // Assume we have a legacy JSON event - err := json.Unmarshal(suggestion, &event) - if err != nil { - return []byte{}, err - } - - if event.CreatedAt.IsZero() { - event.CreatedAt = time.Now().UTC() - } - - eventJSON, err := json.Marshal(event) - if err != nil { - return []byte{}, err - } - - native, _, err := service.codecs["suggestion"].NativeFromTextual(eventJSON) - if err != nil { - return []byte{}, err - } - - binary, err := service.codecs["suggestion"].BinaryFromNative(nil, native) - if err != nil { - return []byte{}, err - } - return binary, nil - } - return suggestion, nil -} - -// SuggestionWorker attempts to work on a suggestion job by redeeming the credentials and emitting the event -type SuggestionWorker interface { - RedeemAndCreateSuggestionEvent(ctx context.Context, credentials []cbr.CredentialRedemption, suggestionText string, suggestion []byte) error - PauseWorker(until time.Time) - IsPaused() bool -} - -// GetCredentialRedemptions as well as total and funding sources from a list of credential bindings -func (service *Service) GetCredentialRedemptions(ctx context.Context, credentials []CredentialBinding) (total decimal.Decimal, requestCredentials []cbr.CredentialRedemption, fundingSources map[string]FundingSource, promotions map[string]*Promotion, err error) { - - // deduplicate the bindings before anything - credentials = DeduplicateCredentialBindings(credentials...) - - total = decimal.Zero - requestCredentials = make([]cbr.CredentialRedemption, len(credentials)) - fundingSources = make(map[string]FundingSource) - promotions = make(map[string]*Promotion) - err = nil - - issuers := make(map[string]*Issuer) - - for i := 0; i < len(credentials); i++ { - var ok bool - var issuer *Issuer - var promotion *Promotion - - publicKey := credentials[i].PublicKey - - if issuer, ok = issuers[publicKey]; !ok { - issuer, err = service.Datastore.GetIssuerByPublicKey(publicKey) - if err != nil || issuer == nil { - e := fmt.Errorf("error finding issuer %s: %w", publicKey, err) - err = errorutils.Wrap(e, e.Error()) - return - } - } - - requestCredentials[i].Issuer = issuer.Name() - requestCredentials[i].TokenPreimage = credentials[i].TokenPreimage - requestCredentials[i].Signature = credentials[i].Signature - - if promotion, ok = promotions[publicKey]; !ok { - promotion, err = service.Datastore.GetPromotion(issuer.PromotionID) - if err != nil || promotion == nil { - e := fmt.Errorf("error finding promotion %s: %w", issuer.PromotionID, err) - err = errorutils.Wrap(e, e.Error()) - return - } - promotions[publicKey] = promotion - } - value := promotion.CredentialValue() - total = total.Add(value) - - fundingSource, ok := fundingSources[publicKey] - fundingSource.Amount = fundingSource.Amount.Add(value) - fundingSource.Credentials = append(fundingSource.Credentials, requestCredentials[i]) - if !ok { - fundingSource.Type = promotion.Type - fundingSource.Cohort = "control" - fundingSource.PromotionID = promotion.ID - } - fundingSources[publicKey] = fundingSource - } - return -} - -// Suggest that a contribution is made -func (service *Service) Suggest(ctx context.Context, credentials []CredentialBinding, suggestionText string) error { - var suggestion Suggestion - err := suggestion.Base64Decode(suggestionText) - if err != nil { - return fmt.Errorf("error decoding suggestion: %w", err) - } - - _, err = govalidator.ValidateStruct(suggestion) - if err != nil { - return err - } - - createdAt, err := time.Now().UTC().MarshalText() - if err != nil { - return err - } - - total, requestCredentials, fundingSources, _, err := service.GetCredentialRedemptions(ctx, credentials) - if err != nil { - return err - } - - fundings := []map[string]interface{}{} - metrics := map[string]decimal.Decimal{} - fundingTypes := []string{} - for _, v := range fundingSources { - val, existed := metrics[v.Type] - if !existed { - fundingTypes = append(fundingTypes, v.Type) - val = decimal.Zero - } - metrics[v.Type] = val.Add(v.Amount) - fundings = append(fundings, map[string]interface{}{ - "type": v.Type, - "cohort": v.Cohort, - "amount": v.Amount.String(), - "promotion": v.PromotionID.String(), - }) - } - - orderID := "" - if suggestion.OrderID != nil { - orderID = suggestion.OrderID.String() - } - - eventMap := map[string]interface{}{ - "id": uuid.NewV4().String(), - "createdAt": string(createdAt), - "channel": suggestion.Channel, - "type": suggestion.Type, - "totalAmount": total.String(), - "orderId": orderID, - "funding": fundings, - } - - eventBinary, err := service.codecs["suggestion"].BinaryFromNative(nil, eventMap) - if err != nil { - return err - } - - err = service.Datastore.InsertSuggestion(requestCredentials, suggestionText, eventBinary) - if err != nil { - return err - } - - for _, fundingType := range fundingTypes { - total := metrics[fundingType] - value, _ := total.Float64() - labels := prometheus.Labels{ - "type": suggestion.Type, - "funding": fundingType, - } - countContributionsTotal.With(labels).Inc() - countContributionsBatTotal.With(labels).Add(value) - } - - if enableSuggestionJob { - asyncCtx, asyncCancel := context.WithTimeout(context.Background(), time.Minute) - ctx = contextutil.Wrap(ctx, asyncCtx) - go func() { - defer asyncCancel() - defer middleware.ConcurrentGoRoutines.With( - prometheus.Labels{ - "method": "SuggestionJob", - }).Dec() - - middleware.ConcurrentGoRoutines.With( - prometheus.Labels{ - "method": "SuggestionJob", - }).Inc() - - _, err := service.Datastore.RunNextSuggestionJob(ctx, service) - if err != nil { - log.Ctx(ctx). - Error(). - Err(err). - Msg("error processing suggestion job") - sentry.CaptureException(errorutils.Wrap(err, "error processing suggestion job")) - } - }() - } - - return nil -} - -// Delete this function once the issue is completed -// https://github.com/brave-intl/bat-go/issues/263 - -// UpdateOrderStatus checks to see if an order has been paid and updates it if so -func (service *Service) UpdateOrderStatus(orderID uuid.UUID) error { - order, err := service.Datastore.GetOrder(orderID) - if err != nil { - return err - } - - sum, err := service.Datastore.GetSumForTransactions(orderID) - if err != nil { - return err - } - - if sum.GreaterThanOrEqual(order.TotalPrice) { - err = service.Datastore.UpdateOrder(orderID, "paid") - if err != nil { - return err - } - } - - return nil -} - -// PauseWorker - pause worker until time specified -func (service *Service) PauseWorker(until time.Time) { - service.pauseSuggestionsUntilMu.Lock() - defer service.pauseSuggestionsUntilMu.Unlock() - service.pauseSuggestionsUntil = until -} - -// IsPaused - is the worker paused? -func (service *Service) IsPaused() bool { - service.pauseSuggestionsUntilMu.RLock() - defer service.pauseSuggestionsUntilMu.RUnlock() - return time.Now().Before(service.pauseSuggestionsUntil) -} - -// RedeemAndCreateSuggestionEvent after validating that all the credential bindings -func (service *Service) RedeemAndCreateSuggestionEvent(ctx context.Context, credentials []cbr.CredentialRedemption, suggestionText string, suggestion []byte) error { - suggestion, err := service.TryUpgradeSuggestionEvent(suggestion) - if err != nil { - return err - } - - // check to see if we skip the cbr redemption case - if skipRedeem, _ := appctx.GetBoolFromContext(ctx, appctx.SkipRedeemCredentialsCTXKey); !skipRedeem { - err = service.cbClient.RedeemCredentials(ctx, credentials, suggestionText) - if err != nil { - // error from cbClient should be errorutils.Codified as data - return err - } - } - - // write the message - err = service.kafkaWriter.WriteMessages(ctx, - kafka.Message{ - Value: suggestion, - }, - ) - if err != nil { - // error from WriteMessages should be errorutils.Codified as data - return errorutils.New(err, "kafka write error", errorutils.Codified{ - ErrCode: "kafka_write", - Retry: true, - }) - } - - // Delete this section once the issue is completed - // https://github.com/brave-intl/bat-go/issues/263 - - newInterface, _, err := service.codecs["suggestion"].NativeFromBinary(suggestion) - eventMap := newInterface.(map[string]interface{}) - if err != nil { - // error should be errorutils.Codified as data - return errorutils.New(err, "kafka codec issue", errorutils.Codified{ - ErrCode: "kafka_codec", - Retry: true, - }) - } - - if eventMap["orderId"] != nil && eventMap["orderId"] != "" { - orderID, err := uuid.FromString(eventMap["orderId"].(string)) - if err != nil { - // error should be errorutils.Codified as data - return errorutils.New(err, "bad order id", errorutils.Codified{ - ErrCode: "bad_order_id", - Retry: false, - }) - } - amount, err := decimal.NewFromString(eventMap["totalAmount"].(string)) - if err != nil { - return errorutils.New(err, "bad total amount value", errorutils.Codified{ - ErrCode: "bad_order_amount", - Retry: false, - }) - } - - _, err = service.Datastore.CreateTransaction(orderID, eventMap["id"].(string), "completed", "BAT", "virtual-grant", amount) - if err != nil { - return errorutils.New(err, "error recording order transaction: ", errorutils.Codified{ - ErrCode: "fail_order_create", - Retry: false, - }) - } - - err = service.UpdateOrderStatus(orderID) - if err != nil { - return errorutils.New(err, "failed to update order status", errorutils.Codified{ - ErrCode: "fail_order_status", - Retry: false, - }) - } - } - - return nil -} diff --git a/services/promotion/suggestions_test.go b/services/promotion/suggestions_test.go deleted file mode 100644 index 631b6a444..000000000 --- a/services/promotion/suggestions_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package promotion - -import ( - "testing" - "time" - - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - "github.com/stretchr/testify/assert" -) - -func TestDeduplicateCredentialBindings(t *testing.T) { - - var tokens = []CredentialBinding{ - { - TokenPreimage: "totally_random", - }, - { - TokenPreimage: "totally_random_1", - }, - { - TokenPreimage: "totally_random", - }, - { - TokenPreimage: "totally_random_2", - }, - } - var seen = []CredentialBinding{} - - var result = DeduplicateCredentialBindings(tokens...) - if len(result) > len(tokens) { - t.Error("result should be less than number of tokens") - } - - for _, v := range result { - for _, vv := range seen { - if v == vv { - t.Error("Deduplication of tokens didn't work") - } - seen = append(seen, v) - } - } -} - -func TestUnmarshalText(t *testing.T) { - encoded := "eyJ0eXBlIjogImF1dG8tY29udHJpYnV0ZSIsICJjaGFubmVsIjogImJyYXZlLmNvbSJ9" - var expected, d Suggestion - expected.Type = "auto-contribute" - expected.Channel = "brave.com" - - err := d.Base64Decode(encoded) - assert.NoError(t, err, "Failed to unmarshal") - assert.Equal(t, expected, d) -} - -func TestTryUpgradeSuggestionEvent(t *testing.T) { - var ( - service Service - err error - ) - - service.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "suggestion": suggestionEventSchema, - }) - - assert.NoError(t, err, "Failed to initialize codecs") - - suggestion := `{"id":"d6e6f7f2-8975-4105-8fef-2ad89e299add","type":"oneoff-tip","channel":"3zsistemi.si","totalAmount":"10","funding":[{"type":"ugp","amount":"10","cohort":"control","promotion":"1d54793b-e8e7-4e96-890f-a1836cab9533"}]}` - - upgraded, err := service.TryUpgradeSuggestionEvent([]byte(suggestion)) - assert.NoError(t, err, "Failed to upgrade suggestion event") - - native, _, err := service.codecs["suggestion"].NativeFromBinary(upgraded) - assert.NoError(t, err) - - createdAt, err := time.Parse(time.RFC3339, native.(map[string]interface{})["createdAt"].(string)) - assert.NoError(t, err) - - assert.True(t, (time.Since(createdAt)) < time.Second) - - suggestionBytes := []byte{72, 100, 54, 101, 54, 102, 55, 102, 50, 45, 56, 57, 55, 53, 45, 52, 49, 48, 53, 45, 56, 102, 101, 102, 45, 50, 97, 100, 56, 57, 101, 50, 57, 57, 97, 100, 100, 20, 111, 110, 101, 111, 102, 102, 45, 116, 105, 112, 24, 51, 122, 115, 105, 115, 116, 101, 109, 105, 46, 115, 105, 60, 50, 48, 49, 57, 45, 49, 49, 45, 49, 53, 84, 50, 51, 58, 50, 57, 58, 49, 49, 46, 49, 52, 49, 52, 48, 53, 56, 55, 53, 90, 4, 49, 48, 2, 6, 117, 103, 112, 4, 49, 48, 14, 99, 111, 110, 116, 114, 111, 108, 72, 49, 100, 53, 52, 55, 57, 51, 98, 45, 101, 56, 101, 55, 45, 52, 101, 57, 54, 45, 56, 57, 48, 102, 45, 97, 49, 56, 51, 54, 99, 97, 98, 57, 53, 51, 51, 0} - - upgraded, err = service.TryUpgradeSuggestionEvent(suggestionBytes) - assert.NoError(t, err, "Failed to upgrade suggestion event") - - assert.Equal(t, suggestionBytes, upgraded) -} diff --git a/services/ratios/cache.go b/services/ratios/cache.go deleted file mode 100644 index dafaabe4e..000000000 --- a/services/ratios/cache.go +++ /dev/null @@ -1,185 +0,0 @@ -package ratios - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/brave-intl/bat-go/libs/clients/coingecko" - ratiosclient "github.com/brave-intl/bat-go/libs/clients/ratios" - "github.com/brave-intl/bat-go/libs/closers" - "github.com/gomodule/redigo/redis" - "github.com/shopspring/decimal" -) - -const ( - // The amount of seconds price data can be in the Redis cache - // before it is considered stale - GetRelativeTTL = 900 -) - -// GetTopCoins - get the top coins -func (s *Service) GetTopCoins(ctx context.Context, limit int) (CoingeckoCoinList, error) { - conn := s.redis.Get() - defer closers.Log(ctx, conn) - - var resp CoingeckoCoinList - coinCacheKey := fmt.Sprintf("coins-%s", time.Now().Format("2006-01-02")) - - tmp, err := redis.Strings(conn.Do("ZREVRANGEBYSCORE", coinCacheKey, "+inf", "0", "LIMIT", "0", limit)) - if err != nil { - return resp, err - } - - list := make([]CoingeckoCoin, len(tmp)) - for i, coin := range tmp { - list[i] = CoingeckoCoin{coin: coin} - } - - resp = CoingeckoCoinList(list) - return resp, nil -} - -// GetTopCurrencies - get the top currencies -func (s *Service) GetTopCurrencies(ctx context.Context, limit int) (CoingeckoVsCurrencyList, error) { - conn := s.redis.Get() - defer closers.Log(ctx, conn) - - var resp CoingeckoVsCurrencyList - currencyCacheKey := fmt.Sprintf("currencies-%s", time.Now().Format("2006-01-02")) - - tmp, err := redis.Strings(conn.Do("ZREVRANGEBYSCORE", currencyCacheKey, "+inf", "0", "LIMIT", "0", limit)) - if err != nil { - return resp, err - } - - list := make([]CoingeckoVsCurrency, len(tmp)) - for i, currency := range tmp { - list[i] = CoingeckoVsCurrency(currency) - } - - resp = CoingeckoVsCurrencyList(list) - return resp, nil -} - -// RecordCoinsAndCurrencies - record the coins and currencies in the cache -func (s *Service) RecordCoinsAndCurrencies(ctx context.Context, coinIds []CoingeckoCoin, vsCurrencies []CoingeckoVsCurrency) error { - conn := s.redis.Get() - defer closers.Log(ctx, conn) - - coinCacheKey := fmt.Sprintf("coins-%s", time.Now().Format("2006-01-02")) - currencyCacheKey := fmt.Sprintf("currencies-%s", time.Now().Format("2006-01-02")) - - for _, coin := range coinIds { - err := conn.Send("ZINCRBY", coinCacheKey, "1", coin.String()) - if err != nil { - return err - } - } - - for _, currency := range vsCurrencies { - err := conn.Send("ZINCRBY", currencyCacheKey, "1", currency.String()) - if err != nil { - return err - } - } - - err := conn.Flush() - if err != nil { - return err - } - - return nil -} - -// CacheRelative - cache the relative values -func (s *Service) CacheRelative(ctx context.Context, resp coingecko.SimplePriceResponse) error { - conn := s.redis.Get() - defer closers.Log(ctx, conn) - - now := time.Now() - - tmp := make([]interface{}, 1, (2*len(resp))+1) - tmp[0] = "relative" - - for coin, rates := range resp { - var subResp ratiosclient.RelativeResponse - payload := make(map[string]map[string]decimal.Decimal, 1) - payload[coin] = rates - subResp.Payload = payload - subResp.LastUpdated = now - - bytes, err := json.Marshal(&subResp) - if err != nil { - return err - } - - tmp = append(tmp, coin) - tmp = append(tmp, bytes) - } - - _, err := conn.Do("HMSET", tmp...) - if err != nil { - return err - } - return nil -} - -// GetRelativeFromCache - get the relative response from the cache -func (s *Service) GetRelativeFromCache(ctx context.Context, vsCurrencies CoingeckoVsCurrencyList, coinIds ...CoingeckoCoin) (*coingecko.SimplePriceResponse, time.Time, error) { - conn := s.redis.Get() - defer closers.Log(ctx, conn) - - updated := time.Now() - - tmp := make([]interface{}, 1, len(coinIds)+1) - tmp[0] = "relative" - for _, coin := range coinIds { - tmp = append(tmp, coin.String()) - } - rates, err := redis.Strings(conn.Do("HMGET", tmp...)) - if err != nil { - return nil, updated, err - } - - resp := make(map[string]map[string]decimal.Decimal, len(rates)) - for i, rate := range rates { - coin := coinIds[i].String() - - if len(rate) > 0 { - var r ratiosclient.RelativeResponse - err = json.Unmarshal([]byte(rate), &r) - if err != nil { - return nil, updated, err - } - // the least recently updated - if r.LastUpdated.Before(updated) { - updated = r.LastUpdated - if time.Since(updated) > GetRelativeTTL*time.Second { - return nil, updated, fmt.Errorf("cached rate is too old: %s", updated) - } - } - - // check that all vs currencies are included - coinRate := r.Payload[coin] - for _, expectedCurrency := range vsCurrencies { - found := false - for includedCurrency := range coinRate { - if expectedCurrency.String() == includedCurrency { - found = true - } - } - if !found { - return nil, updated, fmt.Errorf("missing vs currency: %s", expectedCurrency) - } - } - resp[coin] = coinRate - } else { - return nil, updated, fmt.Errorf("missing rates for coin: %s", coin) - } - } - - sResp := coingecko.SimplePriceResponse(resp) - return &sResp, updated, nil -} diff --git a/services/ratios/cmd/ratios.go b/services/ratios/cmd/ratios.go deleted file mode 100644 index 94e4d800a..000000000 --- a/services/ratios/cmd/ratios.go +++ /dev/null @@ -1,66 +0,0 @@ -package cmd - -import ( - - // pprof imports - _ "net/http/pprof" - - cmdutils "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/services/cmd" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func init() { - ratiosCmd.AddCommand(restCmd) - - // add this command as a serve subcommand - cmd.ServeCmd.AddCommand(ratiosCmd) - - // setup the flags - ratiosCmd.PersistentFlags().String("coingecko-token", "", - "the coingecko service token for this service") - cmdutils.Must(viper.BindPFlag("coingecko-token", ratiosCmd.PersistentFlags().Lookup("coingecko-token"))) - cmdutils.Must(viper.BindEnv("coingecko-token", "COINGECKO_TOKEN")) - - ratiosCmd.PersistentFlags().String("coingecko-service", "https://api.coingecko.com/", "the coingecko service address") - cmdutils.Must(viper.BindPFlag("coingecko-service", ratiosCmd.PersistentFlags().Lookup("coingecko-service"))) - cmdutils.Must(viper.BindEnv("coingecko-service", "COINGECKO_SERVICE")) - - ratiosCmd.PersistentFlags().Int("coingecko-coin-limit", 25, "the coingecko coin limit") - cmdutils.Must(viper.BindPFlag("coingecko-coin-limit", ratiosCmd.PersistentFlags().Lookup("coingecko-coin-limit"))) - cmdutils.Must(viper.BindEnv("coingecko-coin-limit", "COINGECKO_COIN_LIMIT")) - - ratiosCmd.PersistentFlags().Int("coingecko-vs-currency-limit", 5, "the coingecko vs currency limit") - cmdutils.Must(viper.BindPFlag("coingecko-vs-currency-limit", ratiosCmd.PersistentFlags().Lookup("coingecko-vs-currency-limit"))) - cmdutils.Must(viper.BindEnv("coingecko-vs-currency-limit", "COINGECKO_VS_CURRENCY_LIMIT")) - - ratiosCmd.PersistentFlags().String("redis-addr", "redis://localhost:6379", "the redis address") - cmdutils.Must(viper.BindPFlag("redis-addr", ratiosCmd.PersistentFlags().Lookup("redis-addr"))) - cmdutils.Must(viper.BindEnv("redis-addr", "REDIS_ADDR")) - - ratiosCmd.PersistentFlags().Int("rate-limit-per-min", 50, "rate limit per minute value") - cmdutils.Must(viper.BindPFlag("rate-limit-per-min", ratiosCmd.PersistentFlags().Lookup("rate-limit-per-min"))) - cmdutils.Must(viper.BindEnv("rate-limit-per-min", "RATE_LIMIT_PER_MIN")) - - ratiosCmd.PersistentFlags().String("stripe-onramp-secret-key", "", "the stripe service token for this service") - cmdutils.Must(viper.BindPFlag("stripe-onramp-secret-key", ratiosCmd.PersistentFlags().Lookup("stripe-onramp-secret-key"))) - cmdutils.Must(viper.BindEnv("stripe-onramp-secret-key", "STRIPE_ONRAMP_SECRET_KEY")) - - ratiosCmd.PersistentFlags().String("stripe-onramp-server", "https://api.stripe.com/", "the stripe service address") - cmdutils.Must(viper.BindPFlag("stripe-onramp-server", ratiosCmd.PersistentFlags().Lookup("stripe-onramp-server"))) - cmdutils.Must(viper.BindEnv("stripe-onramp-server", "STRIPE_ONRAMP_SERVER")) -} - -var ( - ratiosCmd = &cobra.Command{ - Use: "ratios", - Short: "provides ratios micro-service entrypoint", - } - - restCmd = &cobra.Command{ - Use: "rest", - Short: "provides REST api services", - Run: RestRun, - } -) diff --git a/services/ratios/cmd/rest_run.go b/services/ratios/cmd/rest_run.go deleted file mode 100644 index c6b315ef7..000000000 --- a/services/ratios/cmd/rest_run.go +++ /dev/null @@ -1,99 +0,0 @@ -package cmd - -import ( - "context" - "net/http" - "time" - - // pprof imports - _ "net/http/pprof" - - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/services/cmd" - "github.com/brave-intl/bat-go/services/ratios" - sentry "github.com/getsentry/sentry-go" - "github.com/go-chi/chi" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -// RestRun - Main entrypoint of the REST subcommand -// This function takes a cobra command and starts up the -// ratios rest microservice. -func RestRun(command *cobra.Command, args []string) { - ctx := command.Context() - logger, err := appctx.GetLogger(ctx) - cmdutils.Must(err) - // add profiling flag to enable profiling routes - if viper.GetString("pprof-enabled") != "" { - // pprof attaches routes to default serve mux - // host:6061/debug/pprof/ - go func() { - logger.Error().Err(http.ListenAndServe(":6061", http.DefaultServeMux)) - }() - } - - // add our command line params to context - ctx = context.WithValue(ctx, appctx.CoingeckoServerCTXKey, viper.Get("coingecko-service")) - ctx = context.WithValue(ctx, appctx.CoingeckoAccessTokenCTXKey, viper.Get("coingecko-token")) - ctx = context.WithValue(ctx, appctx.CoingeckoCoinLimitCTXKey, viper.GetInt("coingecko-coin-limit")) - ctx = context.WithValue(ctx, appctx.CoingeckoVsCurrencyLimitCTXKey, viper.GetInt("coingecko-vs-currency-limit")) - ctx = context.WithValue(ctx, appctx.RatiosRedisAddrCTXKey, viper.Get("redis-addr")) - ctx = context.WithValue(ctx, appctx.RateLimitPerMinuteCTXKey, viper.GetInt("rate-limit-per-min")) - - ctx = context.WithValue(ctx, appctx.StripeOnrampSecretKeyCTXKey, viper.Get("stripe-onramp-secret-key")) - ctx = context.WithValue(ctx, appctx.StripeOnrampServerCTXKey, viper.Get("stripe-onramp-server")) - - // setup the service now - ctx, s, err := ratios.InitService(ctx) - if err != nil { - logger.Fatal().Err(err).Msg("failed to initalize ratios service") - } - - // do rest endpoints - r := cmd.SetupRouter(ctx) - r.Get( - "/v2/relative/provider/coingecko/{coinIDs}/{vsCurrencies}/{duration}", - ratios.XBraveHeaderInstrumentHandler( - "GetRelativeHandler", - middleware.InstrumentHandler("GetRelativeHandler", ratios.GetRelativeHandler(s)), - ).ServeHTTP) - r.Get("/v2/history/coingecko/{coinID}/{vsCurrency}/{duration}", middleware.InstrumentHandler("GetHistoryHandler", ratios.GetHistoryHandler(s)).ServeHTTP) - r.Get("/v2/coinmap/provider/coingecko", middleware.InstrumentHandler("GetMappingHandler", ratios.GetMappingHandler(s)).ServeHTTP) - r.Get("/v2/market/provider/coingecko", middleware.InstrumentHandler("GetCoinMarketsHandler", ratios.GetCoinMarketsHandler(s)).ServeHTTP) - r.Post("/v2/stripe/onramp_sessions", middleware.InstrumentHandler( - "StripeOnrampSessionsHandler", - ratios.CreateStripeOnrampSessionsHandler(s)).ServeHTTP, - ) - - err = cmd.SetupJobWorkers(command.Context(), s.Jobs()) - if err != nil { - logger.Fatal().Err(err).Msg("failed to initialize job workers") - } - - // make sure exceptions go to sentry - defer sentry.Flush(time.Second * 2) - - go func() { - err := http.ListenAndServe(":9090", middleware.Metrics()) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("metrics HTTP server start failed!") - } - }() - - // setup server, and run - srv := http.Server{ - Addr: viper.GetString("address"), - Handler: chi.ServerBaseContext(ctx, r), - ReadTimeout: 5 * time.Second, - WriteTimeout: 20 * time.Second, - } - - if err = srv.ListenAndServe(); err != nil { - sentry.CaptureException(err) - logger.Fatal().Err(err).Msg("HTTP server start failed!") - } -} diff --git a/services/ratios/controllers.go b/services/ratios/controllers.go deleted file mode 100644 index d7798eb4b..000000000 --- a/services/ratios/controllers.go +++ /dev/null @@ -1,451 +0,0 @@ -package ratios - -import ( - "errors" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/clients/coingecko" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/go-chi/chi" -) - -// GetRelativeHandler - handler to get current relative exchange rates -func GetRelativeHandler(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - // get context from request - ctx := r.Context() - - var ( - coinIDsInput = chi.URLParam(r, "coinIDs") - vsCurrenciesInput = chi.URLParam(r, "vsCurrencies") - durationInput = chi.URLParam(r, "duration") - err error - ) - - // get logger from context - logger := logging.Logger(ctx, "ratios.GetRelativeHandler") - var coinIDs = new(CoingeckoCoinList) - if err = inputs.DecodeAndValidate(ctx, coinIDs, []byte(coinIDsInput)); err != nil { - if errors.Is(err, ErrCoingeckoCoinInvalid) { - logger.Error().Err(err).Msg("invalid coin input from caller") - return handlers.ValidationError( - "Error validating coin url parameter", - map[string]interface{}{ - "err": err.Error(), - "coinIDs": "invalid coin", - }, - ) - } - - if errors.Is(err, ErrCoingeckoCoinEmpty) { - logger.Error().Err(err).Msg("empty coin input from caller") - return handlers.ValidationError( - "Error validating coin url parameter", - map[string]interface{}{ - "err": err.Error(), - "coinIDs": "empty coin", - }, - ) - } - - if errors.Is(err, ErrCoingeckoCoinListLimit) { - logger.Error().Err(err).Msg("coin list limit exceeded") - return handlers.ValidationError( - "Error validating coin url parameter", - map[string]interface{}{ - "err": err.Error(), - "coinIDs": "coin list limit exceeded", - }, - ) - } - - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - var vsCurrencies = new(CoingeckoVsCurrencyList) - if err = inputs.DecodeAndValidate(ctx, vsCurrencies, []byte(vsCurrenciesInput)); err != nil { - if errors.Is(err, ErrCoingeckoVsCurrencyInvalid) { - logger.Error().Err(err).Msg("invalid vs currency input from caller") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "vScurrencies": "invalid vs currency", - }, - ) - } - - if errors.Is(err, ErrCoingeckoVsCurrencyEmpty) { - logger.Error().Err(err).Msg("empty vs currency input from caller") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "vScurrencies": "empty vs currency", - }, - ) - } - - if errors.Is(err, ErrCoingeckoVsCurrencyLimit) { - logger.Error().Err(err).Msg("vs currency list limit exceeded") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "vScurrencies": "vs currency list limit exceeded", - }, - ) - } - - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - var duration = new(CoingeckoDuration) - if err = inputs.DecodeAndValidate(ctx, duration, []byte(durationInput)); err != nil { - if errors.Is(err, ErrCoingeckoDurationInvalid) { - logger.Error().Err(err).Msg("invalid duration input from caller") - return handlers.ValidationError( - "Error validating duration url parameter", - map[string]interface{}{ - "err": err.Error(), - "duration": "invalid duration", - }, - ) - } - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - rates, err := service.GetRelative(ctx, *coinIDs, *vsCurrencies, *duration) - if err != nil { - logger.Error().Err(err).Msg("failed to get relative exchange rate") - return handlers.WrapError(err, "failed to get relative exchange rate", http.StatusInternalServerError) - } - - // Set Cache-Control header to match when the rates expire in the server cache, - // and would be fetched from Coingecko again. - maxAge := GetRelativeTTL*time.Second - time.Since(rates.LastUpdated) - w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", int(maxAge.Seconds()))) - return handlers.RenderContent(ctx, rates, w, http.StatusOK) - }) -} - -// GetHistoryHandler - handler to get historical exchange rates -func GetHistoryHandler(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - // get context from request - ctx := r.Context() - - var ( - coinIDInput = chi.URLParam(r, "coinID") - vsCurrencyInput = chi.URLParam(r, "vsCurrency") - durationInput = chi.URLParam(r, "duration") - err error - ) - - // get logger from context - logger := logging.Logger(ctx, "ratios.GetHistoryHandler") - - var coinID = new(CoingeckoCoin) - if err = inputs.DecodeAndValidate(ctx, coinID, []byte(coinIDInput)); err != nil { - if errors.Is(err, ErrCoingeckoCoinInvalid) { - logger.Error().Err(err).Msg("invalid coin input from caller") - return handlers.ValidationError( - "Error validating coin url parameter", - map[string]interface{}{ - "err": err.Error(), - "coinIDs": "invalid coin", - }, - ) - } - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - var vsCurrency = new(CoingeckoVsCurrency) - if err = inputs.DecodeAndValidate(ctx, vsCurrency, []byte(vsCurrencyInput)); err != nil { - if errors.Is(err, ErrCoingeckoVsCurrencyInvalid) { - logger.Error().Err(err).Msg("invalid vs currency input from caller") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "vsCurrencies": "invalid vs currency", - }, - ) - } - - if errors.Is(err, ErrCoingeckoVsCurrencyEmpty) { - logger.Error().Err(err).Msg("empty vs currency input from caller") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "vsCurrencies": "empty vs currency", - }, - ) - } - - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - var duration = new(CoingeckoDuration) - if err = inputs.DecodeAndValidate(ctx, duration, []byte(durationInput)); err != nil { - if errors.Is(err, ErrCoingeckoDurationInvalid) { - logger.Error().Err(err).Msg("invalid duration input from caller") - return handlers.ValidationError( - "Error validating duration url parameter", - map[string]interface{}{ - "err": err.Error(), - "duration": "invalid duration", - }, - ) - } - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - rates, err := service.GetHistory(ctx, *coinID, *vsCurrency, *duration) - if err != nil { - logger.Error().Err(err).Msg("failed to get historical exchange rate") - return handlers.WrapError(err, "failed to get historical exchange rate", http.StatusInternalServerError) - } - - w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", duration.ToGetHistoryCacheDurationSeconds())) - return handlers.RenderContent(ctx, rates, w, http.StatusOK) - }) -} - -// MappingResponse - the response structure for the current mappings -type MappingResponse struct { - IDToSymbol map[string]string `json:"idToSymbol"` - SymbolToID map[string]string `json:"symbolToId"` - ContractToID map[string]string `json:"contractToId"` - SupportedVsCurrencies map[string]bool `json:"supportedVsCurrencies"` -} - -// GetMappingHandler - handler to get current coin / currency mappings -func GetMappingHandler(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - resp := MappingResponse{} - - resp.IDToSymbol = ctx.Value(appctx.CoingeckoIDToSymbolCTXKey).(map[string]string) - resp.SymbolToID = ctx.Value(appctx.CoingeckoSymbolToIDCTXKey).(map[string]string) - resp.ContractToID = ctx.Value(appctx.CoingeckoContractToIDCTXKey).(map[string]string) - resp.SupportedVsCurrencies = ctx.Value(appctx.CoingeckoSupportedVsCurrenciesCTXKey).(map[string]bool) - - return handlers.RenderContent(ctx, resp, w, http.StatusOK) - }) -} - -// GetCoinMarketsHandler - handler to get top currency data -func GetCoinMarketsHandler(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - // get context from request - ctx := r.Context() - - var ( - vsCurrencyInput = r.URL.Query().Get("vsCurrency") - limitInput = r.URL.Query().Get("limit") - err error - ) - - // get logger from context - logger := logging.Logger(ctx, "ratios.GetCoinMarketsHandler") - - var vsCurrency = new(CoingeckoVsCurrency) - if err = inputs.DecodeAndValidate(ctx, vsCurrency, []byte(vsCurrencyInput)); err != nil { - if errors.Is(err, ErrCoingeckoVsCurrencyInvalid) { - logger.Error().Err(err).Msg("invalid vs currency input from caller") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "vsCurrency": "invalid vs currency", - }, - ) - } - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - var limit = new(CoingeckoLimit) - if err = inputs.DecodeAndValidate(ctx, limit, []byte(limitInput)); err != nil { - if errors.Is(err, ErrCoingeckoLimitInvalid) { - logger.Error().Err(err).Msg("invalid limit input from caller") - return handlers.ValidationError( - "Error validating vs currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "limit": "invalid limit", - }, - ) - } - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusBadRequest) - } - - data, err := service.GetCoinMarkets(ctx, *vsCurrency, *limit) - if err != nil { - logger.Error().Err(err).Msg("failed to get top currencies") - return handlers.WrapError(err, "failed to get top currencies", http.StatusInternalServerError) - } - - // Set Cache-Control header to match when the market data in the Reis cache expires, - // and would be fetched from Coingecko again. - maxAge := coingecko.CoinMarketsCacheTTLSeconds*time.Second - time.Since(data.LastUpdated) - w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", int(maxAge.Seconds()))) - return handlers.RenderContent(ctx, data, w, http.StatusOK) - }) -} - -// StripeOnrampSessionRequest -type StripeOnrampSessionRequest struct { - WalletAddress string `json:"wallet_address"` - SourceCurrency string `json:"source_currency"` - SourceExchangeAmount string `json:"source_exchange_amount"` - DestinationNetwork string `json:"destination_network"` - DestinationCurrency string `json:"destination_currency"` - SupportedDestinationNetworks []string `json:"supported_destination_networks"` -} - -// CreateStripeOnrampSessionResponse is an HTTP response that includes the Stripe onramp redirect URL -type CreateStripeOnrampSessionResponse struct { - URL string `json:"url"` -} - -func CreateStripeOnrampSessionsHandler(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - logger := logging.Logger(ctx, "ratios.CreateStripeOnrampSessionsHandler") - - // Parse the payload - var req StripeOnrampSessionRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - // Parse SourceExchangeAmount to float64, if it's not empty - var sourceExchangeAmount float64 - if req.SourceExchangeAmount != "" { - var err error - sourceExchangeAmount, err = strconv.ParseFloat(req.SourceExchangeAmount, 64) - if err != nil { - return handlers.WrapError( - fmt.Errorf("SourceExchangeAmount must be a number"), - "SourceExchangeAmount must be a number", - http.StatusBadRequest, - ) - } - - if sourceExchangeAmount < 1 { - return handlers.WrapError( - fmt.Errorf("SourceExchangeAmount must be at least 1"), - "SourceExchangeAmount must be at least 1", - http.StatusBadRequest, - ) - } - - parts := strings.Split(req.SourceExchangeAmount, ".") - if len(parts) == 2 && len(parts[1]) > 2 { - return handlers.WrapError( - fmt.Errorf("SourceExchangeAmount must not include fractions of pennies"), - "SourceExchangeAmount must not include fractions of pennies", - http.StatusBadRequest, - ) - } - } - - // Validate the request payload - supportedDestinationNetworks := []string{"solana", "ethereum", "bitcoin", "polygon"} - supportedDestinationCurrencies := []string{"eth", "matic", "sol", "usdc", "btc"} - - // Check if requested DestinationNetwork is in the supported list - isValidNetwork := false - for _, network := range supportedDestinationNetworks { - if req.DestinationNetwork == network { - isValidNetwork = true - break - } - } - if !isValidNetwork { - return handlers.WrapError( - fmt.Errorf("Invalid destination network: %s", req.DestinationNetwork), - "Invalid destination network", - http.StatusBadRequest, - ) - } - - // Check if all SupportedDestinationNetworks in the request are in the supported list - for _, requestedNetwork := range req.SupportedDestinationNetworks { - isValidNetwork = false - for _, network := range supportedDestinationNetworks { - if requestedNetwork == network { - isValidNetwork = true - break - } - } - if !isValidNetwork { - return handlers.WrapError( - fmt.Errorf("Unsupported network in SupportedDestinationNetworks: %s", requestedNetwork), - "Unsupported network in SupportedDestinationNetworks", - http.StatusBadRequest, - ) - } - } - - // Check if requested DestinationCurrency is in the supported list - isValidCurrency := false - for _, currency := range supportedDestinationCurrencies { - if req.DestinationCurrency == currency { - isValidCurrency = true - break - } - } - if !isValidCurrency { - return handlers.WrapError( - fmt.Errorf("Invalid destination currency: %s", req.DestinationCurrency), - "Invalid destination currency", - http.StatusBadRequest, - ) - } - - // Create a session and retrieve a URL - urlString, err := service.CreateStripeOnrampSessionsHandler( - ctx, - req.WalletAddress, - req.SourceCurrency, - req.SourceExchangeAmount, - req.DestinationNetwork, - req.DestinationCurrency, - req.SupportedDestinationNetworks, - ) - if err != nil { - logger.Error().Err(err).Msg("failed to create on ramp session") - return handlers.WrapError(err, "Failed to create on ramp session", http.StatusInternalServerError) - } - - response := CreateStripeOnrampSessionResponse{URL: urlString} - return handlers.RenderContent(ctx, response, w, http.StatusOK) - }) -} diff --git a/services/ratios/controllers_test.go b/services/ratios/controllers_test.go deleted file mode 100644 index 439e8244a..000000000 --- a/services/ratios/controllers_test.go +++ /dev/null @@ -1,621 +0,0 @@ -//go:build integration -// +build integration - -package ratios_test - -import ( - "bytes" - "context" - "encoding/json" - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/clients/coingecko" - mockcoingecko "github.com/brave-intl/bat-go/libs/clients/coingecko/mock" - ratiosclient "github.com/brave-intl/bat-go/libs/clients/ratios" - "github.com/brave-intl/bat-go/libs/clients/stripe" - mockstripe "github.com/brave-intl/bat-go/libs/clients/stripe/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/services/ratios" - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/gomodule/redigo/redis" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" - "net/http" - "net/http/httptest" - "os" - "regexp" - "strconv" - "testing" - "time" -) - -type ControllersTestSuite struct { - suite.Suite - - ctx context.Context - service *ratios.Service - mockCtrl *gomock.Controller - mockCoingeckoClient *mockcoingecko.MockClient - mockStripeClient *mockstripe.MockClient -} - -func TestControllersTestSuite(t *testing.T) { - suite.Run(t, new(ControllersTestSuite)) -} - -func (suite *ControllersTestSuite) SetupSuite() { - // setup the context - suite.ctx = context.Background() - - // setup debug for client - suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, false) - // setup debug log level - suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "info") - - // setup a logger and put on context - suite.ctx, _ = logutils.SetupLogger(suite.ctx) - - // setup server location - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoServerCTXKey, "https://api.coingecko.com") - // setup token (using public api for tests) - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoAccessTokenCTXKey, "") - // coin limit - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoCoinLimitCTXKey, 2) - // vs-currency limit - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoVsCurrencyLimitCTXKey, 2) - // all this is setup in init service - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoSymbolToIDCTXKey, map[string]string{"bat": "basic-attention-token"}) - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoContractToIDCTXKey, map[string]string{}) - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoIDToSymbolCTXKey, map[string]string{"basic-attention-token": "bat"}) - suite.ctx = context.WithValue(suite.ctx, appctx.CoingeckoSupportedVsCurrenciesCTXKey, map[string]bool{"usd": true}) - - var redisAddr string = "redis://grant-redis:6379" - if len(os.Getenv("REDIS_ADDR")) > 0 { - redisAddr = os.Getenv("REDIS_ADDR") - } - - suite.ctx = context.WithValue(suite.ctx, appctx.RatiosRedisAddrCTXKey, redisAddr) - - govalidator.SetFieldsRequiredByDefault(true) -} - -func (suite *ControllersTestSuite) BeforeTest(sn, tn string) { - suite.mockCtrl = gomock.NewController(suite.T()) - // setup a mock coingecko client - redisAddr, err := appctx.GetStringFromContext(suite.ctx, appctx.RatiosRedisAddrCTXKey) - - redis := &redis.Pool{ - MaxIdle: 3, - IdleTimeout: 240 * time.Second, - // Dial or DialContext must be set. When both are set, DialContext takes precedence over Dial. - Dial: func() (redis.Conn, error) { - return redis.DialURL(redisAddr) - }, - } - - conn := redis.Get() - err = conn.Err() - suite.Require().NoError(err, "failed to setup redis conn") - coingecko := mockcoingecko.NewMockClient(suite.mockCtrl) - suite.mockCoingeckoClient = coingecko - - stripe := mockstripe.NewMockClient(suite.mockCtrl) - suite.mockStripeClient = stripe - - suite.service = ratios.NewService(suite.ctx, coingecko, stripe, redis) - suite.Require().NoError(err, "failed to setup ratios service") -} - -func (suite *ControllersTestSuite) AfterTest(sn, tn string) { - suite.mockCtrl.Finish() -} - -func (suite *ControllersTestSuite) TestGetHistoryHandler() { - handler := ratios.GetHistoryHandler(suite.service) - req, err := http.NewRequest("GET", "/v2/history/coingecko/{coinID}/{vsCurrency}/{duration}", nil) - suite.Require().NoError(err) - - // Test validation errors - // ErrCoingeckoCoinInvalid - req = req.WithContext(suite.ctx) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("coinID", "invalidcoingeckocoin") - rctx.URLParams.Add("vsCurrency", "usd") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoVsCurrencyInvalid - req = req.WithContext(suite.ctx) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "invalidcoingeckovscurrency") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoVsCurrencyEmpty - req = req.WithContext(suite.ctx) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoDurationInvalid - req = req.WithContext(suite.ctx) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "usd") - rctx.URLParams.Add("duration", "invalidcoingeckoduration") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // Test success with 1h duration - suite.mockCoingeckoClient.EXPECT(). - FetchMarketChart(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return(&coingecko.MarketChartResponse{ - Prices: [][]decimal.Decimal{ - []decimal.Decimal{decimal.Zero}, - }, - }, time.Now(), nil) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "usd") - rctx.URLParams.Add("duration", "1h") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - // example - // {"payload":{"prices":[[... - var resp = new(ratios.HistoryResponse) - err = json.Unmarshal(rr.Body.Bytes(), resp) - suite.Require().NoError(err) - suite.Require().True(len(resp.Payload.Prices) > 0) - cacheControl := rr.Header().Get("Cache-Control") - suite.Require().NotEmpty(cacheControl, "Cache-Control header is not present") - maxAgeRegex := regexp.MustCompile(`max-age=(\d+)`) - maxAgeMatch := maxAgeRegex.FindStringSubmatch(cacheControl) - suite.Require().Len(maxAgeMatch, 2, "Invalid max-age parameter in Cache-Control header") - maxAge, err := strconv.Atoi(maxAgeMatch[1]) - suite.Require().Greater(maxAge, 0, "Invalid max-age parameter in Cache-Control header") - suite.Require().LessOrEqual(maxAge, 150, "Invalid max-age parameter in Cache-Control header") - - // Test success with 1d duration - suite.mockCoingeckoClient.EXPECT(). - FetchMarketChart(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return(&coingecko.MarketChartResponse{ - Prices: [][]decimal.Decimal{ - []decimal.Decimal{decimal.Zero}, - }, - }, time.Now(), nil) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "usd") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - var resp1d = new(ratios.HistoryResponse) - err = json.Unmarshal(rr.Body.Bytes(), resp1d) - suite.Require().NoError(err) - suite.Require().True(len(resp1d.Payload.Prices) > 0) - cacheControl = rr.Header().Get("Cache-Control") - suite.Require().NotEmpty(cacheControl, "Cache-Control header is not present") - maxAgeRegex = regexp.MustCompile(`max-age=(\d+)`) - maxAgeMatch = maxAgeRegex.FindStringSubmatch(cacheControl) - suite.Require().Len(maxAgeMatch, 2, "Invalid max-age parameter in Cache-Control header") - maxAge, err = strconv.Atoi(maxAgeMatch[1]) - suite.Require().Greater(maxAge, 150, "Invalid max-age parameter in Cache-Control header") - suite.Require().LessOrEqual(maxAge, 3600, "Invalid max-age parameter in Cache-Control header") - - // Test success with 1w duration - suite.mockCoingeckoClient.EXPECT(). - FetchMarketChart(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return(&coingecko.MarketChartResponse{ - Prices: [][]decimal.Decimal{ - []decimal.Decimal{decimal.Zero}, - }, - }, time.Now(), nil) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "usd") - rctx.URLParams.Add("duration", "1w") - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - var resp1w = new(ratios.HistoryResponse) - err = json.Unmarshal(rr.Body.Bytes(), resp1w) - suite.Require().NoError(err) - suite.Require().True(len(resp1w.Payload.Prices) > 0) - cacheControl = rr.Header().Get("Cache-Control") - suite.Require().NotEmpty(cacheControl, "Cache-Control header is not present") - maxAgeRegex = regexp.MustCompile(`max-age=(\d+)`) - maxAgeMatch = maxAgeRegex.FindStringSubmatch(cacheControl) - suite.Require().Len(maxAgeMatch, 2, "Invalid max-age parameter in Cache-Control header") - maxAge, err = strconv.Atoi(maxAgeMatch[1]) - suite.Require().Greater(maxAge, 3600, "Invalid max-age parameter in Cache-Control header") - suite.Require().LessOrEqual(maxAge, 25200, "Invalid max-age parameter in Cache-Control header") - - // Test success with 1m, 3m, 1y, all durations. - // They should all set a maximum cache-control header of 1 day (86400 seconds) - durations := []string{"1m", "3m", "1y", "all"} - - for _, duration := range durations { - suite.mockCoingeckoClient.EXPECT(). - FetchMarketChart(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return(&coingecko.MarketChartResponse{ - Prices: [][]decimal.Decimal{ - []decimal.Decimal{decimal.Zero}, - }, - }, time.Now(), nil) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinID", "bat") - rctx.URLParams.Add("vsCurrency", "usd") - rctx.URLParams.Add("duration", duration) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - var resp = new(ratios.HistoryResponse) - err = json.Unmarshal(rr.Body.Bytes(), resp) - suite.Require().NoError(err) - suite.Require().True(len(resp.Payload.Prices) > 0) - cacheControl = rr.Header().Get("Cache-Control") - suite.Require().NotEmpty(cacheControl, "Cache-Control header is not present") - maxAgeRegex = regexp.MustCompile(`max-age=(\d+)`) - maxAgeMatch = maxAgeRegex.FindStringSubmatch(cacheControl) - suite.Require().Len(maxAgeMatch, 2, "Invalid max-age parameter in Cache-Control header") - maxAge, err = strconv.Atoi(maxAgeMatch[1]) - suite.Require().Greater(maxAge, 25200, "Invalid max-age parameter in Cache-Control header") - suite.Require().LessOrEqual(maxAge, 86400, "Invalid max-age parameter in Cache-Control header") - } -} - -func (suite *ControllersTestSuite) TestGetRelativeHandler() { - handler := ratios.GetRelativeHandler(suite.service) - // Test validation errors - // ErrCoingeckoCoinInvalid - req, err := http.NewRequest("GET", "/v2/relative/provider/coingecko/{coinIDs}/{vsCurrencies}/{duration}", nil) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("coinIDs", "invalidcoingeckocoin") - rctx.URLParams.Add("vsCurrencies", "usd") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoCoinEmpty - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinIDs", "") - rctx.URLParams.Add("vsCurrencies", "usd") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoCoinListLimit - rctx = chi.NewRouteContext() - coinIDs := "one,celo,sgb,kcs,xdai,metis,wan,bnb,okt,vs,fitfi,tfuel,klay,bch,mtr,eth,kava,fsn,glmr,cro,canto,avax,iotx,doge,rbtc,rose,xdc,fuse,tt,vlx,brise,ulx,evmos,sol,matic,ftm,cet,tlos" - rctx.URLParams.Add("coinIDs", coinIDs) - rctx.URLParams.Add("vsCurrencies", "usd") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoVsCurrencyEmpty - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinIDs", "bat") - rctx.URLParams.Add("vsCurrencies", "") - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoVsCurrencyLimit - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinIDs", "bat") - rctx.URLParams.Add("vsCurrencies", coinIDs) - rctx.URLParams.Add("duration", "1d") - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // ErrCoingeckoDurationInvalid - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinIDs", "bat") - rctx.URLParams.Add("vsCurrencies", "usd") - rctx.URLParams.Add("duration", "invalidcoingeckoduration") - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - suite.Require().Empty(rr.Header().Get("Cache-Control")) - - // Test success case - respy := coingecko.SimplePriceResponse(map[string]map[string]decimal.Decimal{ - "basic-attention-token": map[string]decimal.Decimal{"usd": decimal.Zero}, - }) - suite.mockCoingeckoClient.EXPECT(). - FetchSimplePrice(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return( - &respy, nil) - req, err = http.NewRequest("GET", "/v2/relative/provider/coingecko/{coinIDs}/{vsCurrencies}/{duration}", nil) - suite.Require().NoError(err) - - rctx = chi.NewRouteContext() - rctx.URLParams.Add("coinIDs", "bat") - rctx.URLParams.Add("vsCurrencies", "usd") - rctx.URLParams.Add("duration", "1d") - - // add in our suite ctx - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - // validate response code matches - suite.Require().Equal(http.StatusOK, rr.Code) - // validate cache control header is set and is correct - cacheControl := rr.Header().Get("Cache-Control") - suite.Require().NotEmpty(cacheControl, "Cache-Control header is not present") - maxAgeRegex := regexp.MustCompile(`max-age=(\d+)`) - maxAgeMatch := maxAgeRegex.FindStringSubmatch(cacheControl) - suite.Require().Len(maxAgeMatch, 2, "Invalid max-age parameter in Cache-Control header") - maxAge, err := strconv.Atoi(maxAgeMatch[1]) - suite.Require().NoError(err, "Invalid max-age parameter in Cache-Control header") - suite.Require().Greater(maxAge, 0, "Invalid max-age parameter in Cache-Control header") - suite.Require().LessOrEqual(maxAge, 900, "Invalid max-age parameter in Cache-Control header") - - // example - // {"payload":{"bat":{"usd":1.3,"usd_timeframe_change":0.8356218194962891}} - var resp = new(ratiosclient.RelativeResponse) - err = json.Unmarshal(rr.Body.Bytes(), resp) - suite.Require().NoError(err) - - v, okBat := resp.Payload["bat"] - suite.Require().True(okBat) - - _, okUsd := v["usd"] - suite.Require().True(okUsd) -} - -func (suite *ControllersTestSuite) TestGetCoinMarketsHandler() { - handler := ratios.GetCoinMarketsHandler(suite.service) - coingeckoResp := coingecko.CoinMarketResponse( - []*coingecko.CoinMarket{ - { - ID: "bitcoin", - Symbol: "btc", - Name: "Bitcoin", - Image: "https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579", - MarketCap: 728577821016, - MarketCapRank: 1, - CurrentPrice: 38400, - PriceChange24h: 558.39, - PriceChangePercentage24h: 1.4756, - TotalVolume: 41369367560, - }, - }, - ) - suite.mockCoingeckoClient.EXPECT(). - FetchCoinMarkets(gomock.Any(), gomock.Any(), gomock.Any()). - Return(&coingeckoResp, time.Now(), nil) - req, err := http.NewRequest("GET", "/v2/market/provider/coingecko?vsCurrency=usd&limit=1", nil) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - var resp = new(ratios.GetCoinMarketsResponse) - err = json.Unmarshal(rr.Body.Bytes(), resp) - suite.Require().NoError(err) - suite.Require().Equal(len(resp.Payload), 1) - suite.Require().Equal(resp.Payload[0].Symbol, "btc") - cacheControl := rr.Header().Get("Cache-Control") - suite.Require().NotEmpty(cacheControl, "Cache-Control header is not present") - maxAgeRegex := regexp.MustCompile(`max-age=(\d+)`) - maxAgeMatch := maxAgeRegex.FindStringSubmatch(cacheControl) - suite.Require().Len(maxAgeMatch, 2, "Invalid max-age parameter in Cache-Control header") - maxAge, err := strconv.Atoi(maxAgeMatch[1]) - suite.Require().Greater(maxAge, 0, "Invalid max-age parameter in Cache-Control header") - suite.Require().LessOrEqual(maxAge, 3600, "Invalid max-age parameter in Cache-Control header") -} - -func (suite *ControllersTestSuite) TestCreateStripeOnrampSessionsHandler() { - handler := ratios.CreateStripeOnrampSessionsHandler(suite.service) - // Missing payload results in 400 - { - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", nil) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - } - - // SourceExchangeAmount less than 1 results in 400 - { - payload := &ratios.StripeOnrampSessionRequest{ - WalletAddress: "0x123abc456def", - SourceCurrency: "usd", - SourceExchangeAmount: "0.5", - DestinationNetwork: "ethereum", - DestinationCurrency: "eth", - SupportedDestinationNetworks: []string{"ethereum", "bitcoin", "solana", "polygon"}, - } - payloadBytes, err := json.Marshal(payload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", bytes.NewBuffer(payloadBytes)) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - } - - // SourceExchangeAmount includes fractions of pennies results in 400 - { - payload := &ratios.StripeOnrampSessionRequest{ - WalletAddress: "0x123abc456def", - SourceCurrency: "usd", - SourceExchangeAmount: "1000.001", - DestinationNetwork: "ethereum", - DestinationCurrency: "eth", - SupportedDestinationNetworks: []string{"ethereum", "bitcoin", "solana", "polygon"}, - } - payloadBytes, err := json.Marshal(payload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", bytes.NewBuffer(payloadBytes)) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - } - - // Invalid DestinationNetwork results in 400 - { - payload := &ratios.StripeOnrampSessionRequest{ - WalletAddress: "0x123abc456def", - SourceCurrency: "USD", - SourceExchangeAmount: "1000.00", - DestinationNetwork: "unsupportedNetwork", - DestinationCurrency: "ETH", - SupportedDestinationNetworks: []string{"ethereum", "bitcoin", "solana", "polygon"}, - } - payloadBytes, err := json.Marshal(payload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", bytes.NewBuffer(payloadBytes)) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - } - - // Unsupported network in SupportedDestinationNetworks results in 400 - { - payload := &ratios.StripeOnrampSessionRequest{ - WalletAddress: "0x123abc456def", - SourceCurrency: "usd", - SourceExchangeAmount: "1000.00", - DestinationNetwork: "ethereum", - DestinationCurrency: "eth", - SupportedDestinationNetworks: []string{"ethereum", "binance", "cardano"}, - } - payloadBytes, err := json.Marshal(payload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", bytes.NewBuffer(payloadBytes)) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - } - - // Invalid DestinationCurrency results in 400 - { - payload := &ratios.StripeOnrampSessionRequest{ - WalletAddress: "0x123abc456def", - SourceCurrency: "usd", - SourceExchangeAmount: "1000.00", - DestinationNetwork: "ethereum", - DestinationCurrency: "unsupportedCurrency", - SupportedDestinationNetworks: []string{"ethereum", "bitcoin", "solana", "polygon"}, - } - payloadBytes, err := json.Marshal(payload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", bytes.NewBuffer(payloadBytes)) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - } - - // Valid request yields 200 - { - stripeResp := stripe.OnrampSessionResponse{ - RedirectURL: "https://example.com", - } - suite.mockStripeClient.EXPECT(). - CreateOnrampSession( - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - ). - Return(&stripeResp, nil) - payload := &ratios.StripeOnrampSessionRequest{ - WalletAddress: "0x123abc456def", - SourceCurrency: "usd", - SourceExchangeAmount: "1000.00", - DestinationNetwork: "ethereum", - DestinationCurrency: "eth", - SupportedDestinationNetworks: []string{"ethereum", "solana", "bitcoin"}, - } - payloadBytes, err := json.Marshal(payload) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v2/stripe/onramp_sessions", bytes.NewBuffer(payloadBytes)) - suite.Require().NoError(err) - rctx := chi.NewRouteContext() - req = req.WithContext(suite.ctx) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - var ratiosResp = new(ratios.CreateStripeOnrampSessionResponse) - err = json.Unmarshal(rr.Body.Bytes(), ratiosResp) - suite.Require().NoError(err) - suite.Require().Equal(ratiosResp.URL, "https://example.com") - } -} diff --git a/services/ratios/inputs.go b/services/ratios/inputs.go deleted file mode 100644 index 4fe4be90c..000000000 --- a/services/ratios/inputs.go +++ /dev/null @@ -1,307 +0,0 @@ -package ratios - -import ( - "context" - "errors" - "fmt" - "math" - "strconv" - "strings" - - appctx "github.com/brave-intl/bat-go/libs/context" -) - -// CoingeckoCoin - type for coingecko coin input -type CoingeckoCoin struct { - input string - coin string -} - -// String - stringer implmentation -func (cc *CoingeckoCoin) String() string { - return string(cc.coin) -} - -var ( - // ErrCoingeckoCoinEmpty - empty coin - ErrCoingeckoCoinEmpty = errors.New("coin cannot be empty") - // ErrCoingeckoCoinInvalid - indicates there is a validation issue with the coin - ErrCoingeckoCoinInvalid = errors.New("invalid coin") -) - -// Decode - implement decodable -func (cc *CoingeckoCoin) Decode(ctx context.Context, v []byte) error { - var ( - c string - ok bool - ) - - symbolToID := ctx.Value(appctx.CoingeckoSymbolToIDCTXKey).(map[string]string) - contractToID := ctx.Value(appctx.CoingeckoContractToIDCTXKey).(map[string]string) - - coin := strings.ToLower(string(v)) - if coin == "" { - return ErrCoingeckoCoinEmpty - } - - if c, ok = symbolToID[coin]; !ok { - if c, ok = contractToID[coin]; !ok { - c = coin - } - } - - *cc = CoingeckoCoin{input: coin, coin: c} - return nil -} - -// Validate - implement validatable -func (cc *CoingeckoCoin) Validate(ctx context.Context) error { - idToSymbol := ctx.Value(appctx.CoingeckoIDToSymbolCTXKey).(map[string]string) - - if _, ok := idToSymbol[cc.String()]; ok { - return nil - } - return fmt.Errorf("%w: %s is not valid", ErrCoingeckoCoinInvalid, cc.String()) -} - -var ( - // ErrCoingeckoCoinListLimit - coin list limit - ErrCoingeckoCoinListLimit = errors.New("too many coins passed") -) - -// CoingeckoCoinList - type for coingecko coin list input -type CoingeckoCoinList []CoingeckoCoin - -// String - stringer implmentation -func (ccl *CoingeckoCoinList) String() string { - var s []string - for _, coin := range *ccl { - s = append(s, coin.String()) - } - return strings.Join(s, ",") -} - -// Decode - implement decodable -func (ccl *CoingeckoCoinList) Decode(ctx context.Context, v []byte) error { - c := strings.Split(string(v), ",") - coins := make([]CoingeckoCoin, len(c)) - - for i, coin := range c { - err := coins[i].Decode(ctx, []byte(coin)) - if err != nil { - return err - } - } - *ccl = CoingeckoCoinList(coins) - return nil -} - -// Validate - implement validatable -func (ccl *CoingeckoCoinList) Validate(ctx context.Context) error { - coinLimit := ctx.Value(appctx.CoingeckoCoinLimitCTXKey).(int) - if len(*ccl) > coinLimit { - return fmt.Errorf("%w: %s is not valid", ErrCoingeckoCoinListLimit, ccl.String()) - } - - for _, coin := range *ccl { - err := coin.Validate(ctx) - if err != nil { - return err - } - } - return nil -} - -// CoingeckoVsCurrency - type for coingecko vs currency input -type CoingeckoVsCurrency string - -// String - stringer implmentation -func (cvc *CoingeckoVsCurrency) String() string { - return string(*cvc) -} - -var ( - // ErrCoingeckoVsCurrencyEmpty - empty coin - ErrCoingeckoVsCurrencyEmpty = errors.New("vs currency cannot be empty") - // ErrCoingeckoVsCurrencyInvalid - indicates there is a validation issue with the coin - ErrCoingeckoVsCurrencyInvalid = errors.New("invalid vs currency") -) - -// Decode - implement decodable -func (cvc *CoingeckoVsCurrency) Decode(ctx context.Context, v []byte) error { - vc := strings.ToLower(string(v)) - if vc == "" { - return ErrCoingeckoVsCurrencyEmpty - } - - *cvc = CoingeckoVsCurrency(vc) - return nil -} - -// Validate - implement validatable -func (cvc *CoingeckoVsCurrency) Validate(ctx context.Context) error { - supportedVsCurrencies := ctx.Value(appctx.CoingeckoSupportedVsCurrenciesCTXKey).(map[string]bool) - - if supportedVsCurrencies[cvc.String()] { - return nil - } - return fmt.Errorf("%w: %s is not valid", ErrCoingeckoVsCurrencyInvalid, cvc.String()) -} - -var ( - // ErrCoingeckoVsCurrencyLimit - vs currency list limit - ErrCoingeckoVsCurrencyLimit = errors.New("too many vs currencies passed") -) - -// CoingeckoVsCurrencyList - type for coingecko vs currency list input -type CoingeckoVsCurrencyList []CoingeckoVsCurrency - -// String - stringer implmentation -func (cvcl *CoingeckoVsCurrencyList) String() string { - var s []string - for _, vc := range *cvcl { - s = append(s, vc.String()) - } - return strings.Join(s, ",") -} - -// Decode - implement decodable -func (cvcl *CoingeckoVsCurrencyList) Decode(ctx context.Context, v []byte) error { - c := strings.Split(string(v), ",") - currencies := make([]CoingeckoVsCurrency, len(c)) - - for i, vc := range c { - err := currencies[i].Decode(ctx, []byte(vc)) - if err != nil { - return err - } - } - *cvcl = CoingeckoVsCurrencyList(currencies) - return nil -} - -// Validate - implement validatable -func (cvcl *CoingeckoVsCurrencyList) Validate(ctx context.Context) error { - vsCurrencyLimit := ctx.Value(appctx.CoingeckoVsCurrencyLimitCTXKey).(int) - if len(*cvcl) > vsCurrencyLimit { - return fmt.Errorf("%w: %s is not valid", ErrCoingeckoVsCurrencyLimit, cvcl.String()) - } - - for _, vc := range *cvcl { - err := vc.Validate(ctx) - if err != nil { - return err - } - } - return nil -} - -// CoingeckoDuration - type for coingecko duration input -type CoingeckoDuration string - -// String - stringer implmentation -func (cd *CoingeckoDuration) String() string { - return string(*cd) -} - -// ToDays - get duration in days -func (cd *CoingeckoDuration) ToDays() float32 { - return durationDays[string(*cd)] -} - -// ToGetHistoryCacheDurationSeconds returns the length of time a GetHistory -// cache entry is considered valid based on it's duration -func (cd *CoingeckoDuration) ToGetHistoryCacheDurationSeconds() int { - var defaultCacheDurationSeconds, dayInSeconds int - - // Default cache duration seconds is calculated according to this formula: - // - // Cache time in seconds = 60 * 60 * (fraction of 1 day the duration is) - // - // E.g. - // - // 1h: 60 * 60 * 1/24 = 150 seconds - // 1d: 60 * 60 * 24/24 = 3600 seconds (1 hour) - // 1w: 60 * 60 * 7 = 25200 seconds (7 hours) - // 1m: 60 * 60 * 30 = 108000 seconds (30 hours) - // 3m: 60 * 60 * 90 = 324000 seconds (3.75 days) - // 1y: 60 * 60 * 365 = 31536000 seconds (15.2 days) - // all: 60 * 60 * 365*10 = 315360000 seconds (152 days) - days := cd.ToDays() - defaultCacheDurationSeconds = int(math.Round(float64(60 * 60 * days))) - - // For any duration that would have a default cache duration greater - // than 1 day, use 1 day instead. - dayInSeconds = 60 * 60 * 24 - if defaultCacheDurationSeconds > dayInSeconds { - return dayInSeconds - } - return defaultCacheDurationSeconds -} - -var ( - durationDays = map[string]float32{ - "live": 1.0 / 24, - "1h": 1.0 / 24, - "1d": 1, - "1w": 7, - "1m": 30, - "3m": 3 * 30, - "1y": 365, - "all": 10 * 365, - } - - // ErrCoingeckoDurationInvalid - indicates there is a validation issue with the duration - ErrCoingeckoDurationInvalid = errors.New("invalid duration") - - // ErrCoingeckoLimitInvalid - indicates there is a validation issue with the Limit - ErrCoingeckoLimitInvalid = errors.New("invalid limit") -) - -// Decode - implement decodable -func (cd *CoingeckoDuration) Decode(ctx context.Context, v []byte) error { - d := strings.ToLower(string(v)) - *cd = CoingeckoDuration(d) - return nil -} - -// Validate - implement validatable -func (cd *CoingeckoDuration) Validate(ctx context.Context) error { - if _, ok := durationDays[cd.String()]; ok { - return nil - } - return fmt.Errorf("%w: %s is not valid", ErrCoingeckoDurationInvalid, cd.String()) -} - -// CoingeckoLimit - type for number of results per page -// Note: we only will request the first page -type CoingeckoLimit int - -// String - stringer implmentation -func (cl *CoingeckoLimit) String() string { - return strconv.Itoa(int(*cl)) -} - -// Int - int conversion implmentation -func (cl *CoingeckoLimit) Int() int { - return int(*cl) -} - -// Decode - implement decodable -func (cl *CoingeckoLimit) Decode(ctx context.Context, v []byte) error { - l, err := strconv.Atoi(string(v)) - if err != nil { - return err - } - - *cl = CoingeckoLimit(l) - return nil -} - -// Validate - implement validatable -func (cl *CoingeckoLimit) Validate(ctx context.Context) error { - if !(0 < cl.Int() && cl.Int() <= 250) { - return fmt.Errorf("%w: %s is not valid", ErrCoingeckoLimitInvalid, cl.String()) - } - return nil -} diff --git a/services/ratios/mapping.go b/services/ratios/mapping.go deleted file mode 100644 index d15ff32c7..000000000 --- a/services/ratios/mapping.go +++ /dev/null @@ -1,219 +0,0 @@ -package ratios - -import ( - "context" - "strings" - - "github.com/brave-intl/bat-go/libs/clients/coingecko" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/shopspring/decimal" -) - -var ( - special = map[string]string{ - "dai": "dai", - "imx": "immutable-x", - "abat": "aave-bat", - "abusd": "aave-busd", - "adai": "aave-dai", - "adx": "adex", - "aenj": "aave-enj", - "aknc": "aave-knc", - "alink": "aave-link", - "amana": "aave-mana", - "amkr": "aave-mkr", - "aren": "aave-ren", - "art": "maecenas", - "asnx": "aave-snx", - "ast": "airswap", - "asusd": "aave-susd", - "atusd": "aave-tusd", - "ausdc": "aave-usdc", - "ausdt": "aave-usdt", - "awbtc": "aave-wbtc", - "ayfi": "aave-yfi", - "azrx": "aave-zrx", - "bat": "basic-attention-token", - "bcp": "bitcashpay-old", - "bee": "bee-coin", - "bnb": "binancecoin", - "boa": "bosagora", - "bob": "bobs_repair", - "boo": "boo", - "booty": "candybooty", - "box": "box-token", - "btu": "btu-protocol", - "can": "canyacoin", - "cat": "bitclave", - "comp": "compound-governance-token", - "cor": "coreto", - "cvp": "concentrated-voting-power", - "cwbtc": "compound-wrapped-btc", - "data": "streamr", - "dg": "degate", - "drc": "dracula-token", - "dream": "dreamteam", - "drt": "domraider", - "duck": "dlp-duck-token", - "edg": "edgeless", - "ert": "eristica", - "eth": "ethereum", - "flx": "reflexer-ungovernance-token", - "frm": "ferrum-network", - "ftm": "fantom", - "game": "gamecredits", - "gen": "daostack", - "get": "get-token", - "gold": "dragonereum-gold", - "grt": "the-graph", - "gtc": "gitcoin", - "hex": "hex", - "hgt": "hellogold", - "hot": "holotoken", - "ieth": "iethereum", - "imp": "ether-kingdoms-token", - "inx": "infinitx", - "iotx": "iotex", - "isla": "insula", - "jet": "jetcoin", - "key": "selfkey", - "land": "landshare", - "like": "likecoin", - "link": "chainlink", - "lnk": "chainlink", - "luna": "wrapped-terra", - "mana": "decentraland", - "mdx": "mandala-exchange-token", - "mm": "million", - "mta": "meta", - "musd": "musd", - "muso": "mirrored-united-states-oil-fund", - "nct": "polyswarm", - "ndx": "ndex", - "neon": "neon", - "oil": "oiler", - "one": "menlo-one", - "ousd": "origin-dollar", - "pla": "playdapp", - "plat": "dash-platinum", - "play": "herocoin", - "pmon": "polychain-monsters", - "poly": "polymath", - "prt": "portion", - "rai": "rai", - "rbc": "rubic", - "ren": "republic-protocol", - "rfr": "refereum", - "sand": "san-diego-coin", - "sbtc": "sbtc", - "sdt": "stabledoc-token", - "seth": "seth", - "sgtv2": "sharedstake-governance-token", - "sig": "signal-token", - "soul": "cryptosoul", - "space": "spacelens", - "spn": "sapien", - "spnd": "spendcoin", - "star": "filestar", - "steth": "staked-ether", - "susd": "stabilize-usd", - "swt": "swarm-city", - "tbtc": "tbtc", - "tether": "tether", - "thor": "thor", - "time": "chronobank", - "top": "top-network", - "uni": "uniswap", - "usdn": "neutrino", - "usdp": "paxos-standard", - "usdx": "usdx", - "ust": "wrapped-ust", - "val": "sora-validator-token", - "wings": "wings", - "xor": "sora", - "yld": "yield", - "zap": "zap", - } -) - -func (s *Service) initializeCoingeckoCurrencies(ctx context.Context) (context.Context, error) { - var ( - idToSymbol = map[string]string{} - symbolToID = map[string]string{} - contractToID = map[string]string{} - supportedVsCurrencies = map[string]bool{} - ) - - tmp, err := s.coingecko.FetchSupportedVsCurrencies(ctx) - if err != nil { - return ctx, err - } - for _, supported := range *tmp { - supportedVsCurrencies[supported] = true - } - - list, err := s.coingecko.FetchCoinList(ctx, true) - if err != nil { - return ctx, err - } - - for _, coin := range *list { - if specialID, ok := special[coin.Symbol]; ok && specialID != coin.ID { - continue - } - - if coin.ID != "link" { - idToSymbol[coin.ID] = coin.Symbol - } - symbolToID[coin.Symbol] = coin.ID - - if len(coin.Platforms.Ethereum) > 0 { - contractToID[strings.ToLower(coin.Platforms.Ethereum)] = coin.ID - } - } - - ctx = context.WithValue(ctx, appctx.CoingeckoIDToSymbolCTXKey, idToSymbol) - ctx = context.WithValue(ctx, appctx.CoingeckoSymbolToIDCTXKey, symbolToID) - ctx = context.WithValue(ctx, appctx.CoingeckoContractToIDCTXKey, contractToID) - ctx = context.WithValue(ctx, appctx.CoingeckoSupportedVsCurrenciesCTXKey, supportedVsCurrencies) - - return ctx, nil -} - -func mapSimplePriceResponse(ctx context.Context, resp coingecko.SimplePriceResponse, duration CoingeckoDuration, coinIDs CoingeckoCoinList, vsCurrencies CoingeckoVsCurrencyList) coingecko.SimplePriceResponse { - out := map[string]map[string]decimal.Decimal{} - - for k, v := range resp { - innerOut := map[string]decimal.Decimal{} - for kk, rate := range v { - var foundCurrency bool - for _, vv := range []CoingeckoVsCurrency(vsCurrencies) { - if strings.HasPrefix(kk, string(vv)) { - foundCurrency = true - break - } - } - if !foundCurrency { - // exclude non-matching currencies - continue - } - - if strings.HasSuffix(kk, "_24h_change") { - if duration != CoingeckoDuration("1d") { - // skip key if duration is mismatched - continue - } - kk = strings.ReplaceAll(kk, "_24h_change", "_timeframe_change") - } - innerOut[kk] = rate - } - - for _, vv := range []CoingeckoCoin(coinIDs) { - if vv.String() == k { - out[vv.input] = innerOut - } - } - } - - return coingecko.SimplePriceResponse(out) -} diff --git a/services/ratios/middleware.go b/services/ratios/middleware.go deleted file mode 100644 index 38a05569d..000000000 --- a/services/ratios/middleware.go +++ /dev/null @@ -1,50 +0,0 @@ -package ratios - -import ( - "net/http" - "os" - "strconv" - - "github.com/prometheus/client_golang/prometheus" -) - -var ( - xBraveKeyHeaderPresentCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "x_brave_key_header_count", - Help: "A count of the requests by whether the x-brave-key header is present", - }, - []string{"handler", "present"}, - ) -) - -func init() { - if err := prometheus.Register(xBraveKeyHeaderPresentCounter); err != nil { - if ae, ok := err.(prometheus.AlreadyRegisteredError); ok { - xBraveKeyHeaderPresentCounter = ae.ExistingCollector.(*prometheus.CounterVec) - } - } -} - -// XBraveHeaderInstrumentHandler instruments an http.Handler to capture -// data relevant to the ratios service -func XBraveHeaderInstrumentHandler(name string, next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - key := r.Header.Get("x-brave-key") - expectedKey := os.Getenv("X_BRAVE_KEY") - - var present bool - if expectedKey == "" { - present = key != "" - } else { - present = key == expectedKey - } - - xBraveKeyHeaderPresentCounter.With(prometheus.Labels{ - "present": strconv.FormatBool(present), - "handler": name, - }).Inc() - - next.ServeHTTP(w, r) - }) -} diff --git a/services/ratios/service.go b/services/ratios/service.go deleted file mode 100644 index 1e81ebf4f..000000000 --- a/services/ratios/service.go +++ /dev/null @@ -1,311 +0,0 @@ -package ratios - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/clients/coingecko" - ratiosclient "github.com/brave-intl/bat-go/libs/clients/ratios" - "github.com/brave-intl/bat-go/libs/clients/stripe" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - logutils "github.com/brave-intl/bat-go/libs/logging" - srv "github.com/brave-intl/bat-go/libs/service" - "github.com/gomodule/redigo/redis" - "github.com/shopspring/decimal" -) - -// NewService - create a new ratios service structure -func NewService( - ctx context.Context, - coingecko coingecko.Client, - stripe stripe.Client, - redis *redis.Pool, -) *Service { - return &Service{ - jobs: []srv.Job{}, - coingecko: coingecko, - stripe: stripe, - redis: redis, - } -} - -// Service contains datastore -type Service struct { - jobs []srv.Job - // coingecko client - coingecko coingecko.Client - stripe stripe.Client - redis *redis.Pool -} - -// Jobs - Implement srv.JobService interface -func (s *Service) Jobs() []srv.Job { - return s.jobs -} - -// InitService creates a service using the passed context -func InitService(ctx context.Context) (context.Context, *Service, error) { - // get logger from context - logger := logging.Logger(ctx, "ratios.InitService") - - redisAddr, err := appctx.GetStringFromContext(ctx, appctx.RatiosRedisAddrCTXKey) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize the redis client") - return ctx, nil, fmt.Errorf("failed to initialize redis client: %w", err) - } - - redis := &redis.Pool{ - MaxIdle: 3, - IdleTimeout: 240 * time.Second, - // Dial or DialContext must be set. When both are set, DialContext takes precedence over Dial. - Dial: func() (redis.Conn, error) { - return redis.DialURL(redisAddr) - }, - } - - conn := redis.Get() - defer func() { - err := conn.Close() - logutils.Logger(ctx, "ratios.InitService").Error().Err(err).Msg("failed to close redis conn") - }() - err = conn.Err() - if err != nil { - logger.Error().Err(err).Msg("failed to initialize the redis client") - return ctx, nil, fmt.Errorf("failed to initialize redis client: %w", err) - } - - coingecko, err := coingecko.NewWithContext(ctx, redis) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize the coingecko client") - return ctx, nil, fmt.Errorf("failed to initialize coingecko client: %w", err) - } - - stripe, err := stripe.NewWithContext(ctx) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize the stripe client") - return ctx, nil, fmt.Errorf("failed to initialize stripe client: %w", err) - } - - service := NewService(ctx, coingecko, stripe, redis) - - ctx, err = service.initializeCoingeckoCurrencies(ctx) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize the coingecko coin mappings") - return ctx, nil, fmt.Errorf("failed to initialize coingecko coin mappings: %w", err) - } - - service.jobs = []srv.Job{ - { - Func: service.RunNextRelativeCachePrepopulationJob, - Cadence: 5 * time.Minute, - Workers: 1, - }, - } - - // Sigh, for compatibility with existing ratios mistakes - decimal.MarshalJSONWithoutQuotes = true - - return ctx, service, nil -} - -// RunNextRelativeCachePrepopulationJob takes the next job to prepopulate the relative cache and completes it -func (s *Service) RunNextRelativeCachePrepopulationJob(ctx context.Context) (bool, error) { - topCoins, err := s.GetTopCoins(ctx, 500) - if err != nil { - return true, fmt.Errorf("failed to retrieve top coins: %w", err) - } - topCurrencies, err := s.GetTopCurrencies(ctx, 5) - if err != nil { - return true, fmt.Errorf("failed to retrieve top currencies: %w", err) - } - - if len(topCoins) == 0 || len(topCurrencies) == 0 { - return false, nil - } - - rates, err := s.coingecko.FetchSimplePrice(ctx, topCoins.String(), topCurrencies.String(), true) - if err != nil { - return true, fmt.Errorf("failed to fetch price from coingecko: %w", err) - } - - err = s.CacheRelative(ctx, *rates) - if err != nil { - return true, fmt.Errorf("failed to cache relative rates: %w", err) - } - - return true, nil -} - -// GetRelative - respond to caller with the relative exchange rates -func (s *Service) GetRelative( - ctx context.Context, - coinIDs CoingeckoCoinList, - vsCurrencies CoingeckoVsCurrencyList, - duration CoingeckoDuration, -) (*ratiosclient.RelativeResponse, error) { - // get logger from context - logger := logging.Logger(ctx, "ratios.GetRelative") - - // record coin / currency usage - err := s.RecordCoinsAndCurrencies(ctx, []CoingeckoCoin(coinIDs), []CoingeckoVsCurrency(vsCurrencies)) - if err != nil { - logger.Error().Err(err).Msg("failed to record coin / currency statistics") - } - - // attempt to fetch from cache - rates, updated, err := s.GetRelativeFromCache(ctx, vsCurrencies, []CoingeckoCoin(coinIDs)...) - if err != nil || rates == nil { - if err != nil { - logger.Debug().Err(err).Msg("failed to fetch cached relative rates") - } - rates, err = s.coingecko.FetchSimplePrice(ctx, coinIDs.String(), vsCurrencies.String(), true) - if err != nil { - logger.Error().Err(err).Msg("failed to fetch price from coingecko") - return nil, fmt.Errorf("failed to fetch price from coingecko: %w", err) - } - updated = time.Now() - } - - if duration != "1d" { - // fill change with 0s ( it's unused for multiple coinIDs and we will overwrite for single ) - out := map[string]map[string]decimal.Decimal{} - for k, v := range *rates { - innerOut := map[string]decimal.Decimal{} - for kk, vv := range v { - if !strings.HasSuffix(kk, "_24h_change") { - innerOut[kk+"_timeframe_change"] = decimal.Zero - innerOut[kk] = vv - } - } - out[k] = innerOut - } - - if len(coinIDs) == 1 { - // request history for duration to calculate change - chart, _, err := s.coingecko.FetchMarketChart( - ctx, - coinIDs[0].String(), - vsCurrencies[0].String(), - duration.ToDays(), - duration.ToGetHistoryCacheDurationSeconds(), - ) - if err != nil { - logger.Error().Err(err).Msg("failed to fetch chart from coingecko") - return nil, fmt.Errorf("failed to fetch chart from coingecko: %w", err) - } - - current := out[coinIDs[0].String()][vsCurrencies[0].String()] - previous := chart.Prices[0][1] - change := decimal.Zero - // division by error when previous is zero - if !previous.IsZero() { - change = current.Sub(previous).Div(previous).Mul(decimal.NewFromFloat(100)) - } - - out[coinIDs[0].String()][vsCurrencies[0].String()+"_timeframe_change"] = change - } - - tmp := coingecko.SimplePriceResponse(out) - rates = &tmp - } - - return &ratiosclient.RelativeResponse{ - Payload: mapSimplePriceResponse(ctx, *rates, duration, coinIDs, vsCurrencies), - LastUpdated: updated, - }, nil -} - -// HistoryResponse - the response structure for history calls -type HistoryResponse struct { - Payload coingecko.MarketChartResponse `json:"payload"` - LastUpdated time.Time `json:"lastUpdated"` -} - -// GetHistory - respond to caller with historical exchange rates -func (s *Service) GetHistory(ctx context.Context, coinID CoingeckoCoin, vsCurrency CoingeckoVsCurrency, duration CoingeckoDuration) (*HistoryResponse, error) { - // get logger from context - logger := logging.Logger(ctx, "ratios.GetHistory") - - err := s.RecordCoinsAndCurrencies(ctx, []CoingeckoCoin{coinID}, []CoingeckoVsCurrency{vsCurrency}) - if err != nil { - logger.Error().Err(err).Msg("failed to record coin / currency statistics") - } - - chart, updated, err := s.coingecko.FetchMarketChart( - ctx, - coinID.String(), - vsCurrency.String(), - duration.ToDays(), - duration.ToGetHistoryCacheDurationSeconds(), - ) - if err != nil { - logger.Error().Err(err).Msg("failed to fetch chart from coingecko") - return nil, fmt.Errorf("failed to fetch chart from coingecko: %w", err) - } - - return &HistoryResponse{ - Payload: *chart, - LastUpdated: updated, - }, nil -} - -// GetCoinMarketsResponse - the response structure for top currency calls -type GetCoinMarketsResponse struct { - Payload coingecko.CoinMarketResponse `json:"payload"` - LastUpdated time.Time `json:"lastUpdated"` -} - -// GetCoinMarkets - respond to caller with top currencies -func (s *Service) GetCoinMarkets( - ctx context.Context, - vsCurrency CoingeckoVsCurrency, - limit CoingeckoLimit, -) (*GetCoinMarketsResponse, error) { - - // get logger from context - logger := logging.Logger(ctx, "ratios.GetCoinMarkets") - - payload, updated, err := s.coingecko.FetchCoinMarkets(ctx, vsCurrency.String(), limit.Int()) - if err != nil { - logger.Error().Err(err).Msg("failed to fetch coin markets data from coingecko") - return nil, fmt.Errorf("failed to fetch coin markets data from coingecko: %w", err) - } - - return &GetCoinMarketsResponse{ - Payload: *payload, - LastUpdated: updated, - }, nil -} - -// CreateStripeOnrampSessionsHandler - respond to caller with an onramp URL -func (s *Service) CreateStripeOnrampSessionsHandler( - ctx context.Context, - walletAddress string, - sourceCurrency string, - sourceExchangeAmount string, - destinationNetwork string, - destinationCurrency string, - supportedDestinationNetworks []string, -) (string, error) { - logger := logging.Logger(ctx, "ratios.CreateStripeOnrampSessionsHandler") - payload, err := s.stripe.CreateOnrampSession( - ctx, - "redirect", - walletAddress, - sourceCurrency, - sourceExchangeAmount, - destinationNetwork, - destinationCurrency, - supportedDestinationNetworks, - ) - - if err != nil { - logger.Error().Err(err).Msg("failed to create onramp session with stripe") - return "", fmt.Errorf("error creating onramp session with stripe: %w", err) - } - - return payload.RedirectURL, nil -} diff --git a/services/rewards/Dockerfile b/services/rewards/Dockerfile deleted file mode 100644 index fa5829315..000000000 --- a/services/rewards/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM bat-go:latest -CMD ["bat-go", "serve", "rewards", "rest"] diff --git a/services/rewards/README.md b/services/rewards/README.md deleted file mode 100644 index f950ea7ae..000000000 --- a/services/rewards/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Rewards Microservice - -## Purpose - -To provide BAT-Rewards API. - -### to run - -go run ../main.go serve rewards diff --git a/services/rewards/choices.go b/services/rewards/choices.go deleted file mode 100644 index 2010492aa..000000000 --- a/services/rewards/choices.go +++ /dev/null @@ -1,99 +0,0 @@ -package rewards - -import ( - "context" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/shopspring/decimal" -) - -var ( - // the default choices are derived by the formula using - // the two tables below. - - // example: - // if BAT to USD ratio is 0.2 - // lookup the index of the price increments that matches - // use that index number as the choice list from - // the choice table as the "default" autocontribute choices - // returned in the rewards parameters - choiceTable = [][]float64{ - {3, 5, 7, 10, 20}, - {4, 6, 9, 12, 25}, - {5, 8, 11, 17, 35}, - {6, 10, 14, 20, 40}, - {9, 12, 20, 35, 50}, - {15, 25, 35, 50, 100}, - {20, 35, 50, 85, 120}, - {30, 50, 70, 100, 120}, - } - priceIncrements = []float64{ - 1, 0.8, 0.6, 0.5, 0.35, 0.2, 0.15, 0.1, - } - defaultTipChoices = []float64{ - 1, 10, 100, - } - defaultMonthlyChoices = []float64{ - 1, 10, 100, - } -) - -func getChoices(ctx context.Context, ratio decimal.Decimal) []float64 { - // get logger from context - logger := logging.Logger(ctx, "rewards.getChoices") - - // if we have DefaultACChoices in the context, just return that. - if acChoices, ok := ctx.Value(appctx.DefaultACChoicesCTXKey).([]float64); ok { - return acChoices - } - - // find the price increment given our ratio - var ( - index = -1 - ) - - var rate, exact = ratio.Float64() - if !exact { - logger.Debug(). - Float64("rate", rate). - Msg("rate was not exact, down sampled to float64") - } - - for i, v := range priceIncrements { - if v <= rate { - // this is the index we want from our choice table - index = i - break - } - } - if index < 0 || index >= len(priceIncrements) { - // use the last index if no matches - index = len(priceIncrements) - 1 - } - return choiceTable[index] -} - -func getTipChoices(ctx context.Context) []float64 { - c, ok := ctx.Value(appctx.DefaultTipChoicesCTXKey).([]float64) - if !ok { - return defaultTipChoices - } - return c -} - -func getDefaultChoice(ctx context.Context) float64 { - c, ok := ctx.Value(appctx.DefaultACChoiceCTXKey).(float64) - if !ok { - return 0 - } - return c -} - -func getMonthlyChoices(ctx context.Context) []float64 { - c, ok := ctx.Value(appctx.DefaultMonthlyChoicesCTXKey).([]float64) - if !ok { - return defaultMonthlyChoices - } - return c -} diff --git a/services/rewards/cmd/rest_run.go b/services/rewards/cmd/rest_run.go deleted file mode 100644 index 8f9c82d78..000000000 --- a/services/rewards/cmd/rest_run.go +++ /dev/null @@ -1,107 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "net/http" - "time" - - // pprof imports - _ "net/http/pprof" - - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/services/cmd" - "github.com/brave-intl/bat-go/services/rewards" - sentry "github.com/getsentry/sentry-go" - "github.com/go-chi/chi" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -// RestRun - Main entrypoint of the REST subcommand -// This function takes a cobra command and starts up the -// rewards rest microservice. -func RestRun(command *cobra.Command, args []string) { - ctx := command.Context() - logger, err := appctx.GetLogger(ctx) - cmdutils.Must(err) - // add profiling flag to enable profiling routes - if viper.GetString("pprof-enabled") != "" { - // pprof attaches routes to default serve mux - // host:6061/debug/pprof/ - go func() { - logger.Error().Err(http.ListenAndServe(":6061", http.DefaultServeMux)) - }() - } - - // add our command line params to context - ctx = context.WithValue(ctx, appctx.RatiosServerCTXKey, viper.Get("ratios-service")) - ctx = context.WithValue(ctx, appctx.RatiosAccessTokenCTXKey, viper.Get("ratios-token")) - ctx = context.WithValue(ctx, appctx.BaseCurrencyCTXKey, viper.Get("base-currency")) - ctx = context.WithValue(ctx, appctx.RatiosCacheExpiryDurationCTXKey, viper.GetDuration("ratios-client-cache-expiry")) - ctx = context.WithValue(ctx, appctx.RatiosCachePurgeDurationCTXKey, viper.GetDuration("ratios-client-cache-purge")) - ctx = context.WithValue(ctx, appctx.DefaultACChoiceCTXKey, viper.GetFloat64("default-ac-choice")) - ctx = context.WithValue(ctx, appctx.ParametersMergeBucketCTXKey, viper.Get("merge-param-bucket")) - - ctx = context.WithValue(ctx, appctx.ParametersVBATDeadlineCTXKey, viper.GetTime("vbat-deadline")) - ctx = context.WithValue(ctx, appctx.ParametersTransitionCTXKey, viper.GetBool("transition")) - // parse default-monthly-choices and default-tip-choices - - var monthlyChoices = []float64{} - if err := viper.UnmarshalKey("default-monthly-choices", &monthlyChoices); err != nil { - logger.Fatal().Err(err).Msg("failed to parse default-monthly-choices") - } - ctx = context.WithValue(ctx, appctx.DefaultMonthlyChoicesCTXKey, monthlyChoices) - - var tipChoices = []float64{} - if err := viper.UnmarshalKey("default-tip-choices", &tipChoices); err != nil { - logger.Fatal().Err(err).Msg("failed to parse default-tip-choices") - } - ctx = context.WithValue(ctx, appctx.DefaultTipChoicesCTXKey, tipChoices) - - var acChoices = []float64{} - if err := viper.UnmarshalKey("default-ac-choices", &acChoices); err != nil { - logger.Fatal().Err(err).Msg("failed to parse default-ac-choices") - } - if len(acChoices) > 0 { - ctx = context.WithValue(ctx, appctx.DefaultACChoicesCTXKey, acChoices) - } - - // setup the service now - s, err := rewards.InitService(ctx) - if err != nil { - logger.Fatal().Err(err).Msg("failed to initalize rewards service") - } - logger.Info().Str("service", fmt.Sprintf("%+v", s)).Msg("initialized service") - - // do rest endpoints - r := cmd.SetupRouter(ctx) - r.Get("/v1/parameters", middleware.InstrumentHandler( - "GetParametersHandler", rewards.GetParametersHandler(s)).ServeHTTP) - - // make sure exceptions go to sentry - defer sentry.Flush(time.Second * 2) - - go func() { - err := http.ListenAndServe(":9090", middleware.Metrics()) - if err != nil { - sentry.CaptureException(err) - logger.Panic().Err(err).Msg("metrics HTTP server start failed!") - } - }() - - // setup server, and run - srv := http.Server{ - Addr: viper.GetString("address"), - Handler: chi.ServerBaseContext(ctx, r), - ReadTimeout: 5 * time.Second, - WriteTimeout: 20 * time.Second, - } - - if err = srv.ListenAndServe(); err != nil { - sentry.CaptureException(err) - logger.Fatal().Err(err).Msg("HTTP server start failed!") - } -} diff --git a/services/rewards/cmd/rewards.go b/services/rewards/cmd/rewards.go deleted file mode 100644 index 6b59c7abc..000000000 --- a/services/rewards/cmd/rewards.go +++ /dev/null @@ -1,81 +0,0 @@ -package cmd - -import ( - - // pprof imports - _ "net/http/pprof" - - cmdutils "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/services/cmd" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func init() { - rewardsCmd.AddCommand(restCmd) - - // add this command as a serve subcommand - cmd.ServeCmd.AddCommand(rewardsCmd) - - // setup the flags - // vbat-deadline - defaults to "" - rewardsCmd.PersistentFlags().String("vbat-deadline", "", - "vbat deadline parameter") - cmdutils.Must(viper.BindPFlag("vbat-deadline", rewardsCmd.PersistentFlags().Lookup("vbat-deadline"))) - cmdutils.Must(viper.BindEnv("vbat-deadline", "VBAT_DEADLINE")) - - // transition- defaults to false - rewardsCmd.PersistentFlags().Bool("transition", false, - "transition parameter") - cmdutils.Must(viper.BindPFlag("transition", rewardsCmd.PersistentFlags().Lookup("transition"))) - cmdutils.Must(viper.BindEnv("transition", "TRANSITION")) - - // merge_param_bucket - defaults to "" - rewardsCmd.PersistentFlags().String("merge-param-bucket", "", - "the bucket for which parameters are merged into this service") - cmdutils.Must(viper.BindPFlag("merge-param-bucket", rewardsCmd.PersistentFlags().Lookup("merge-param-bucket"))) - cmdutils.Must(viper.BindEnv("merge-param-bucket", "MERGE_PARAM_BUCKET")) - - // defaultCurrency - defaults to USD - rewardsCmd.PersistentFlags().String("default-currency", "USD", - "the default base currency for the rewards system") - cmdutils.Must(viper.BindPFlag("default-currency", rewardsCmd.PersistentFlags().Lookup("default-currency"))) - cmdutils.Must(viper.BindEnv("default-currency", "DEFAULT_CURRENCY")) - - // defaultTipChoices - defaults to 1,10,100 - rewardsCmd.PersistentFlags().String("default-tip-choices", `1,10,100`, - "the default tip choices for the rewards system") - cmdutils.Must(viper.BindPFlag("default-tip-choices", rewardsCmd.PersistentFlags().Lookup("default-tip-choices"))) - cmdutils.Must(viper.BindEnv("default-tip-choices", "DEFAULT_TIP_CHOICES")) - - // defaultMonthlyChoices - defaults to 1,10,100 - rewardsCmd.PersistentFlags().String("default-monthly-choices", `1,10,100`, - "the default monthly choices for the rewards system") - cmdutils.Must(viper.BindPFlag("default-monthly-choices", rewardsCmd.PersistentFlags().Lookup("default-monthly-choices"))) - cmdutils.Must(viper.BindEnv("default-monthly-choices", "DEFAULT_MONTHLY_CHOICES")) - - // defaultACChoices - defaults to empty (which causes the choices to be dynamic) - rewardsCmd.PersistentFlags().String("default-ac-choices", "", - "the default ac choices for the rewards system") - cmdutils.Must(viper.BindPFlag("default-ac-choices", rewardsCmd.PersistentFlags().Lookup("default-ac-choices"))) - cmdutils.Must(viper.BindEnv("default-ac-choices", "DEFAULT_AC_CHOICES")) - - // defaultACChoice - defaults to empty (which causes the choices to be dynamic) - rewardsCmd.PersistentFlags().String("default-ac-choice", "", - "the default ac choice for the rewards system") - cmdutils.Must(viper.BindPFlag("default-ac-choice", rewardsCmd.PersistentFlags().Lookup("default-ac-choice"))) - cmdutils.Must(viper.BindEnv("default-ac-choice", "DEFAULT_AC_CHOICE")) -} - -var ( - rewardsCmd = &cobra.Command{ - Use: "rewards", - Short: "provides rewards micro-service entrypoint", - } - - restCmd = &cobra.Command{ - Use: "rest", - Short: "provides REST api services", - Run: RestRun, - } -) diff --git a/services/rewards/controllers.go b/services/rewards/controllers.go deleted file mode 100644 index 4ab78dde1..000000000 --- a/services/rewards/controllers.go +++ /dev/null @@ -1,56 +0,0 @@ -package rewards - -import ( - "errors" - "net/http" - - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" -) - -// GetParametersHandler - handler to get reward parameters -func GetParametersHandler(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - // get context from request - ctx := r.Context() - - var ( - currencyInput = r.URL.Query().Get("currency") - parameters *ParametersV1 - err error - ) - - if currencyInput == "" { - currencyInput = "USD" - } - - // get logger from context - logger := logging.Logger(ctx, "rewards.GetParametersHandler") - - // in here we need to validate our currency - var currency = new(BaseCurrency) - if err = inputs.DecodeAndValidate(ctx, currency, []byte(currencyInput)); err != nil { - if errors.Is(err, ErrBaseCurrencyInvalid) { - logger.Error().Err(err).Msg("invalid currency input from caller") - return handlers.ValidationError( - "Error validating currency url parameter", - map[string]interface{}{ - "err": err.Error(), - "currency": "invalid currency", - }, - ) - } - // degraded, unknown error when validating/decoding - logger.Error().Err(err).Msg("unforseen error in decode and validation") - return handlers.WrapError(err, "degraded: ", http.StatusInternalServerError) - } - - parameters, err = service.GetParameters(ctx, currency) - if err != nil { - logger.Error().Err(err).Msg("failed to get reward parameters") - return handlers.WrapError(err, "failed to get parameters", http.StatusInternalServerError) - } - return handlers.RenderContent(ctx, parameters, w, http.StatusOK) - }) -} diff --git a/services/rewards/controllers_test.go b/services/rewards/controllers_test.go deleted file mode 100644 index 40aef1ffc..000000000 --- a/services/rewards/controllers_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package rewards - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "io" - "net/http" - "net/http/httptest" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/shopspring/decimal" - - "github.com/brave-intl/bat-go/libs/clients/ratios" - ratiosmock "github.com/brave-intl/bat-go/libs/clients/ratios/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/go-chi/chi" - gomock "github.com/golang/mock/gomock" -) - -type mockGetObjectAPI func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) - -func (m mockGetObjectAPI) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { - return m(ctx, params, optFns...) -} - -func TestGetParametersController(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - mockRatios := ratiosmock.NewMockClient(mockCtrl) - mockRatios.EXPECT().FetchRate(gomock.Any(), gomock.Eq("bat"), gomock.Eq("usd")). - Return(&ratios.RateResponse{ - Payload: map[string]decimal.Decimal{ - "usd": decimal.Zero, - "bat": decimal.Zero, - }}, nil) - - var mockS3PayoutStatus = mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { - return &s3.GetObjectOutput{ - Body: io.NopCloser(bytes.NewBufferString(`{ - "uphold":"processing", - "gemini":"off", - "bitflyer":"off", - "unverified":"off" - } - `)), - }, nil - }) - - var mockS3CustodianRegions = mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { - return &s3.GetObjectOutput{ - Body: io.NopCloser(bytes.NewBufferString(`{ - "uphold": { - "allow": [], - "block": [] - }, - "gemini": { - "allow": [], - "block": [] - }, - "bitflyer": { - "allow": [], - "block": [] - } - }`)), - }, nil - }) - - var mockS3 = mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { - if *params.Key == "payout-status.json" { - return mockS3PayoutStatus(ctx, params, optFns...) - } else if *params.Key == "custodian-regions.json" { - return mockS3CustodianRegions(ctx, params, optFns...) - } - return nil, errors.New("invalid key") - }) - - var ( - s, err = NewService(context.Background(), mockRatios, mockS3) - hGet = GetParametersHandler(s) - params = new(ParametersV1) - ) - if err != nil { - t.Error("failed to make new service: ", err) - } - - req, err := http.NewRequest("GET", "/parameters", nil) - if err != nil { - t.Error("failed to make new request: ", err) - } - - rctx := chi.NewRouteContext() - r := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - // add the parameters merge bucket to context - r = req.WithContext(context.WithValue(r.Context(), appctx.ParametersMergeBucketCTXKey, "something")) - - rrGet := httptest.NewRecorder() - hGet.ServeHTTP(rrGet, r) - if rrGet.Code != http.StatusOK { - t.Log("result: ", rrGet.Body.String()) - t.Error("was expecting an ok response: ", rrGet.Code) - } - - if err = json.Unmarshal(rrGet.Body.Bytes(), ¶ms); err != nil { - t.Error("should be no error with unmarshalling response: ", err) - } - - if params.BATRate != 0 { - t.Error("was expecting 0 for the bat rate") - } - - if params.PayoutStatus == nil || params.PayoutStatus.Uphold != "processing" { - t.Error("was expecting uphold to be set to processing") - } - if len(params.AutoContribute.Choices) == 0 { - t.Error("was expecting more than one ac choices") - } -} diff --git a/services/rewards/docker-compose.yml b/services/rewards/docker-compose.yml deleted file mode 100644 index 522521a8f..000000000 --- a/services/rewards/docker-compose.yml +++ /dev/null @@ -1,63 +0,0 @@ -version: "3.4" - -networks: - rewards: - driver: bridge - -services: - # dev-refresh service will start up a rewards server bound to host port 3343 - # which allows one to do `docker restart rewards-dev-refresh` when the user - # wants to "restart" the service running new code. This is especially helpful - # when you hook it up to `fswatch` type utilities, causing a re-run of `go run` - # every time a file changes. - rewards-dev: - container_name: rewards-dev - image: 045014719567.dkr.ecr.us-west-2.amazonaws.com/rewards:latest - ports: - - "3333:3333" - security_opt: - - no-new-privileges:true - read_only: true - volumes: - - ../:/src - working_dir: /src - networks: - - rewards - environment: - - PPROF_ENABLED - - ENV=local - - ADDR=:3333 - - RATIOS_SERVICE="https://ratios.rewards.bravesoftware.com" - - RATIOS_TOKEN - - DEFAULT_TIP_CHOICES=1,10,100 - - DEFAULT_MONTHLY_CHOICES=1,10,100 - - DEFAULT_AC_CHOICES - - DEFAULT_AC_CHOICE - - DEFAULT_CURRENCY=USD - - RATIOS_CACHE_EXPIRY=1m - - RATIOS_CACHE_PURGE=2m - - rewards-dev-refresh: - container_name: rewards-dev-refresh - image: golang:1.19 - ports: - - "3343:3343" - - "6061:6061" - command: "cd main && go run main.go serve rewards rest" - volumes: - - ../:/src - working_dir: /src - networks: - - rewards - environment: - - PPROF_ENABLED - - ENV=local - - ADDR=:3343 - - RATIOS_SERVICE="https://ratios.rewards.bravesoftware.com" - - RATIOS_TOKEN - - DEFAULT_AC_CHOICES - - DEFAULT_TIP_CHOICES=1,10,100 - - DEFAULT_MONTHLY_CHOICES=1,10,100 - - DEFAULT_CURRENCY=USD - - RATIOS_CACHE_EXPIRY=1m - - RATIOS_CACHE_PURGE=2m diff --git a/services/rewards/inputs.go b/services/rewards/inputs.go deleted file mode 100644 index 6a6b1e4f1..000000000 --- a/services/rewards/inputs.go +++ /dev/null @@ -1,252 +0,0 @@ -package rewards - -import ( - "context" - "errors" - "fmt" - "strings" -) - -// BaseCurrency - type for base currency input -type BaseCurrency string - -// String - stringer implmentation -func (rbc *BaseCurrency) String() string { - return string(*rbc) -} - -var ( - validCurrencies = map[string]bool{ - "AED": true, - "AFN": true, - "ALL": true, - "AMD": true, - "ANG": true, - "AOA": true, - "ARS": true, - "AUD": true, - "AWG": true, - "AZN": true, - "BAM": true, - "BBD": true, - "BDT": true, - "BGN": true, - "BHD": true, - "BIF": true, - "BMD": true, - "BND": true, - "BOB": true, - "BRL": true, - "BSD": true, - "BTC": true, - "BTN": true, - "BWP": true, - "BYN": true, - "BZD": true, - "CAD": true, - "CDF": true, - "CHF": true, - "CLF": true, - "CLP": true, - "CNH": true, - "CNY": true, - "COP": true, - "CRC": true, - "CUC": true, - "CUP": true, - "CVE": true, - "CZK": true, - "DJF": true, - "DKK": true, - "DOP": true, - "DZD": true, - "EGP": true, - "ERN": true, - "ETB": true, - "EUR": true, - "FJD": true, - "FKP": true, - "GBP": true, - "GEL": true, - "GGP": true, - "GHS": true, - "GIP": true, - "GMD": true, - "GNF": true, - "GTQ": true, - "GYD": true, - "HKD": true, - "HNL": true, - "HRK": true, - "HTG": true, - "HUF": true, - "IDR": true, - "ILS": true, - "IMP": true, - "INR": true, - "IQD": true, - "IRR": true, - "ISK": true, - "JEP": true, - "JMD": true, - "JOD": true, - "JPY": true, - "KES": true, - "KGS": true, - "KHR": true, - "KMF": true, - "KPW": true, - "KRW": true, - "KWD": true, - "KYD": true, - "KZT": true, - "LAK": true, - "LBP": true, - "LKR": true, - "LRD": true, - "LSL": true, - "LYD": true, - "MAD": true, - "MDL": true, - "MGA": true, - "MKD": true, - "MMK": true, - "MNT": true, - "MOP": true, - "MRO": true, - "MRU": true, - "MUR": true, - "MVR": true, - "MWK": true, - "MXN": true, - "MYR": true, - "MZN": true, - "NAD": true, - "NGN": true, - "NIO": true, - "NOK": true, - "NPR": true, - "NZD": true, - "OMR": true, - "PAB": true, - "PEN": true, - "PGK": true, - "PHP": true, - "PKR": true, - "PLN": true, - "PYG": true, - "QAR": true, - "RON": true, - "RSD": true, - "RUB": true, - "RWF": true, - "SAR": true, - "SBD": true, - "SCR": true, - "SDG": true, - "SEK": true, - "SGD": true, - "SHP": true, - "SLL": true, - "SOS": true, - "SRD": true, - "SSP": true, - "STD": true, - "STN": true, - "SVC": true, - "SYP": true, - "SZL": true, - "THB": true, - "TJS": true, - "TMT": true, - "TND": true, - "TOP": true, - "TRY": true, - "TTD": true, - "TWD": true, - "TZS": true, - "UAH": true, - "UGX": true, - "USD": true, - "UYU": true, - "UZS": true, - "VEF": true, - "VES": true, - "VND": true, - "VUV": true, - "WST": true, - "XAF": true, - "XAG": true, - "XAU": true, - "XCD": true, - "XDR": true, - "XOF": true, - "XPD": true, - "XPF": true, - "XPT": true, - "YER": true, - "ZAR": true, - "ZMW": true, - "ZWL": true, - "ADA": true, - "ATOM": true, - "BAT": true, - "BCH": true, - "BTG": true, - "-DAI": true, - "DASH": true, - "DCR": true, - "DGB": true, - "DOGE": true, - "EOS": true, - "ETH": true, - "IOTA": true, - "LBA": true, - "LINK": true, - "LTC": true, - "NANO": true, - "NEO": true, - "OMG": true, - "STORM": true, - "TRX": true, - "-TUSD": true, - "UPBTC": true, - "UPEUR": true, - "UPT": true, - "UPUSD": true, - "-USDC": true, - "-USDT": true, - "VOX": true, - "XEM": true, - "XLM": true, - "XRP": true, - "ZIL": true, - "ZRX": true, - "BCC": true, - } -) - -var ( - // ErrBaseCurrencyEmpty - empty base currency - ErrBaseCurrencyEmpty = errors.New("currency cannot be empty") - // ErrBaseCurrencyInvalid - indicates there is a validation issue with the currency - ErrBaseCurrencyInvalid = errors.New("invalid currency") -) - -// Decode - implement decodable -func (rbc *BaseCurrency) Decode(ctx context.Context, v []byte) error { - s := string(v) - if s == "" { - return ErrBaseCurrencyEmpty - } - *rbc = BaseCurrency(strings.ToUpper(s)) - return nil -} - -// Validate - implement validatable -func (rbc *BaseCurrency) Validate(ctx context.Context) error { - if validCurrencies[rbc.String()] { - return nil - } - return fmt.Errorf("%w: %s is not valid", ErrBaseCurrencyInvalid, rbc.String()) -} diff --git a/services/rewards/json-schema/example-parameters-response.json b/services/rewards/json-schema/example-parameters-response.json deleted file mode 100644 index 2b2a76076..000000000 --- a/services/rewards/json-schema/example-parameters-response.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "batRate": 1.43, - "autocontribute": { - "defaultChoice": 20, - "choices": [ - 5, 10, 15, 20, 25, 50, 100 - ] - }, - "tips": { - "defaultTipChoices": [ - 1, 10, 100 - ], - "defaultMonthlyChoices": [ - 1, 10, 100 - ] - } -} diff --git a/services/rewards/json-schema/example-parameters-response.schema b/services/rewards/json-schema/example-parameters-response.schema deleted file mode 100644 index 5cc7b3162..000000000 --- a/services/rewards/json-schema/example-parameters-response.schema +++ /dev/null @@ -1,47 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema", - "$id": "https://rewards.brave.com/parameters.json", - "title": "Rewards parameters schema", - "type": "object", - "properties": { - "batRate": { - "type": "number", - "minimum": 0 - }, - "autocontribute": { - "type": "object", - "properties": { - "defaultChoice": { - "type": "number", - "minimum": 0 - }, - "choices": { - "type": "array", - "items": { - "type": "number", - "minimum": 0 - } - } - } - }, - "tips": { - "type": "object", - "properties": { - "defaultTipChoices": { - "type": "array", - "items": { - "type": "number", - "minimum": 0 - } - } - "defaultMonthlychoices": { - "type": "array", - "items": { - "type": "number", - "minimum": 0 - } - } - } - } - } -} diff --git a/services/rewards/parameters.go b/services/rewards/parameters.go deleted file mode 100644 index 5608b4b19..000000000 --- a/services/rewards/parameters.go +++ /dev/null @@ -1,30 +0,0 @@ -package rewards - -import ( - "time" - - "github.com/brave-intl/bat-go/libs/custodian" -) - -// AutoContribute - reward parameters about ac (votes) -type AutoContribute struct { - Choices []float64 `json:"choices,omitempty"` - DefaultChoice float64 `json:"defaultChoice,omitempty"` -} - -// Tips - reward parameters about tips (suggestions) -type Tips struct { - DefaultTipChoices []float64 `json:"defaultTipChoices,omitempty"` - DefaultMonthlyChoices []float64 `json:"defaultMonthlyChoices,omitempty"` -} - -// ParametersV1 - structure of reward parameters -type ParametersV1 struct { - PayoutStatus *custodian.PayoutStatus `json:"payoutStatus"` - CustodianRegions *custodian.Regions `json:"custodianRegions"` - BATRate float64 `json:"batRate,omitempty"` - AutoContribute AutoContribute `json:"autocontribute,omitempty"` - Tips Tips `json:"tips,omitempty"` - Transition bool `json:"vbatExpired"` - VBATDeadline *time.Time `json:"vbatDeadline,omitempty"` -} diff --git a/services/rewards/protobuf/rewards.proto b/services/rewards/protobuf/rewards.proto deleted file mode 100644 index 094bbd70e..000000000 --- a/services/rewards/protobuf/rewards.proto +++ /dev/null @@ -1,246 +0,0 @@ -syntax = "proto3"; - -// Rewards - This is the gRPC definition of the rewards service -service Rewards { - // GetParameters - This RPC call will retrieve the - // valid rewards parameters from the service and return - // a parameters response instance - rpc GetParameters(ParametersRequest) returns(ParametersResponse) {} -} - -// ParametersRequest - Primary type of the parameters -// get endpoint request. -message ParametersRequest { - Currency currency = 1; -} - -// ParametersResponse - Primary type of the parameters -// get endpoint response. Contains fee, autocontribute -// and tip settings for the rewards system -message ParametersResponse { - double batRate = 1; - AutoContribute autocontribute = 2; - Tips tips = 3; -} - -// AutoContribute - autocontribute settings -message AutoContribute { - double defaultChoice = 1; - repeated double choices = 2; -} - -// Tips - tip settings -message Tips { - repeated double defaultTipChoices = 1; - repeated double defaultMonthlyChoices = 2; -} - -enum Currency { - AED = 0; - AFN = 1; - ALL = 2; - AMD = 3; - ANG = 4; - AOA = 5; - ARS = 6; - AUD = 7; - AWG = 8; - AZN = 9; - BAM = 10; - BBD = 11; - BDT = 12; - BGN = 13; - BHD = 14; - BIF = 15; - BMD = 16; - BND = 17; - BOB = 18; - BRL = 19; - BSD = 20; - BTC = 21; - BTN = 22; - BWP = 23; - BYN = 24; - BZD = 25; - CAD = 26; - CDF = 27; - CHF = 28; - CLF = 29; - CLP = 30; - CNH = 31; - CNY = 32; - COP = 33; - CRC = 34; - CUC = 35; - CUP = 36; - CVE = 37; - CZK = 38; - DJF = 39; - DKK = 40; - DOP = 41; - DZD = 42; - EGP = 43; - ERN = 44; - ETB = 45; - EUR = 46; - FJD = 47; - FKP = 48; - GBP = 49; - GEL = 50; - GGP = 51; - GHS = 52; - GIP = 53; - GMD = 54; - GNF = 55; - GTQ = 56; - GYD = 57; - HKD = 58; - HNL = 59; - HRK = 60; - HTG = 61; - HUF = 62; - IDR = 63; - ILS = 64; - IMP = 65; - INR = 66; - IQD = 67; - IRR = 68; - ISK = 69; - JEP = 70; - JMD = 71; - JOD = 72; - JPY = 73; - KES = 74; - KGS = 75; - KHR = 76; - KMF = 77; - KPW = 78; - KRW = 79; - KWD = 80; - KYD = 81; - KZT = 82; - LAK = 83; - LBP = 84; - LKR = 85; - LRD = 86; - LSL = 87; - LYD = 88; - MAD = 89; - MDL = 90; - MGA = 91; - MKD = 92; - MMK = 93; - MNT = 94; - MOP = 95; - MRO = 96; - MRU = 97; - MUR = 98; - MVR = 99; - MWK = 100; - MXN = 101; - MYR = 102; - MZN = 103; - NAD = 104; - NGN = 105; - NIO = 106; - NOK = 107; - NPR = 108; - NZD = 109; - OMR = 110; - PAB = 111; - PEN = 112; - PGK = 113; - PHP = 114; - PKR = 115; - PLN = 116; - PYG = 117; - QAR = 118; - RON = 119; - RSD = 120; - RUB = 121; - RWF = 122; - SAR = 123; - SBD = 124; - SCR = 125; - SDG = 126; - SEK = 127; - SGD = 128; - SHP = 129; - SLL = 130; - SOS = 131; - SRD = 132; - SSP = 133; - STD = 134; - STN = 135; - SVC = 136; - SYP = 137; - SZL = 138; - THB = 139; - TJS = 140; - TMT = 141; - TND = 142; - TOP = 143; - TRY = 144; - TTD = 145; - TWD = 146; - TZS = 147; - UAH = 148; - UGX = 149; - USD = 150; - UYU = 151; - UZS = 152; - VEF = 153; - VES = 154; - VND = 155; - VUV = 156; - WST = 157; - XAF = 158; - XAG = 159; - XAU = 160; - XCD = 161; - XDR = 162; - XOF = 163; - XPD = 164; - XPF = 165; - XPT = 166; - YER = 167; - ZAR = 168; - ZMW = 169; - ZWL = 170; - ADA = 171; - ATOM = 172; - BAT = 173; - BCH = 174; - BTG = 175; - -DAI = 176; - DASH = 177; - DCR = 178; - DGB = 179; - DOGE = 180; - EOS = 181; - ETH = 182; - IOTA = 183; - LBA = 184; - LINK = 185; - LTC = 186; - NANO = 187; - NEO = 188; - OMG = 189; - STORM = 190; - TRX = 191; - -TUSD = 192; - UPBTC = 193; - UPEUR = 194; - UPT = 195; - UPUSD = 196; - -USDC = 197; - -USDT = 198; - VOX = 199; - XEM = 200; - XLM = 201; - XRP = 202; - ZIL = 203; - ZRX = 204; - BCC = 205; -} - diff --git a/services/rewards/service.go b/services/rewards/service.go deleted file mode 100644 index fe19f3a3d..000000000 --- a/services/rewards/service.go +++ /dev/null @@ -1,179 +0,0 @@ -package rewards - -import ( - "context" - "errors" - "fmt" - "strings" - "sync" - "time" - - "github.com/aws/aws-sdk-go-v2/service/s3" - appaws "github.com/brave-intl/bat-go/libs/aws" - "github.com/brave-intl/bat-go/libs/clients/ratios" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - srv "github.com/brave-intl/bat-go/libs/service" -) - -// NewService - create a new rewards service structure -func NewService(ctx context.Context, ratio ratios.Client, s3client appaws.S3GetObjectAPI) (*Service, error) { - logger := logging.Logger(ctx, "rewards.NewService") - - cfg, err := appaws.BaseAWSConfig(ctx, logger) - if err != nil { - return nil, fmt.Errorf("failed to create base aws config: %w", err) - } - - logger.Info().Msg("checking s3 client") - if s3client == nil { - s3client = s3.NewFromConfig(cfg) - } - - logger.Info().Str("s3client", fmt.Sprintf("%+v", s3client)).Msg("setup s3 client") - return &Service{ - cacheMu: new(sync.RWMutex), - jobs: []srv.Job{}, - ratios: ratio, - s3Client: s3client, - }, nil -} - -// Service contains datastore -type Service struct { - lastPollTime time.Time - lastPayoutStatus *custodian.PayoutStatus - lastCustodianRegions *custodian.Regions - cacheMu *sync.RWMutex - - jobs []srv.Job - // ratios client - ratios ratios.Client - s3Client appaws.S3GetObjectAPI -} - -// Jobs - Implement srv.JobService interface -func (s *Service) Jobs() []srv.Job { - return s.jobs -} - -// InitService creates a service using the passed context -func InitService(ctx context.Context) (*Service, error) { - // get logger from context - logger := logging.Logger(ctx, "rewards.InitService") - - // get from ratios the current bat rate - client, err := ratios.NewWithContext(ctx) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize the ratios client") - return nil, fmt.Errorf("failed to initialize ratios client: %w", err) - } - - logger.Info().Msg("creating new rewards parameters service") - - return NewService(ctx, client, nil) -} - -// GetParameters - respond to caller with the rewards parameters -func (s *Service) GetParameters(ctx context.Context, currency *BaseCurrency) (*ParametersV1, error) { - if currency == nil { - currency = new(BaseCurrency) - *currency = "usd" - } - - var currencyStr = strings.ToLower(currency.String()) - // get logger from context - logger := logging.Logger(ctx, "rewards.GetParameters") - - rateData, err := s.ratios.FetchRate(ctx, "bat", currencyStr) - if err != nil { - logger.Error().Err(err).Msg("failed to fetch rate from ratios") - return nil, fmt.Errorf("failed to fetch rate from ratios: %w", err) - } - if rateData == nil { - logger.Error().Msg("empty response from ratios") - return nil, errors.New("empty response from ratios") - } - - var choices = getChoices(ctx, rateData.Payload[currencyStr]) - var defaultChoice float64 - if len(choices) > 1 { - defaultChoice = choices[len(choices)/2] - } else if len(choices) > 0 { - defaultChoice = choices[0] - } - - // if there is a default choice configured use it - if dc := getDefaultChoice(ctx); dc > 0 { - defaultChoice = dc - } - - var rate, _ = rateData.Payload[currencyStr].Float64() - - s.cacheMu.RLock() - lastPollTime := s.lastPollTime - s.cacheMu.RUnlock() - - if time.Now().After(lastPollTime.Add(15 * time.Minute)) { - // merge in static s3 attributes into response - var ( - payoutStatus *custodian.PayoutStatus - custodianRegions *custodian.Regions - bucket, ok = ctx.Value(appctx.ParametersMergeBucketCTXKey).(string) - ) - logger.Debug().Str("bucket", bucket).Msg("merge bucket env var") - if ok { - // get payout status - logger.Debug().Str("bucket", bucket).Msg("extracting payout status") - payoutStatus, err = custodian.ExtractPayoutStatus(ctx, s.s3Client, bucket) - if err != nil { - return nil, fmt.Errorf("failed to get payout status parameters: %w", err) - } - logger.Debug().Str("bucket", bucket).Str("payout status", fmt.Sprintf("%+v", *payoutStatus)).Msg("payout status") - - // get the custodian regions - logger.Debug().Str("bucket", bucket).Msg("extracting custodian regions") - custodianRegions, err = custodian.ExtractCustodianRegions(ctx, s.s3Client, bucket) - if err != nil { - return nil, fmt.Errorf("failed to get custodian regions parameters: %w", err) - } - logger.Debug().Str("bucket", bucket).Str("custodian regions", fmt.Sprintf("%+v", *custodianRegions)).Msg("custodianRegions") - } - s.cacheMu.Lock() - s.lastPayoutStatus = payoutStatus // update the payout status - s.lastCustodianRegions = custodianRegions // update the custodian regions - s.lastPollTime = time.Now() // update the time to now - s.cacheMu.Unlock() - } - s.cacheMu.RLock() - defer s.cacheMu.RUnlock() - payoutStatus := s.lastPayoutStatus - custodianRegions := s.lastCustodianRegions - - params := &ParametersV1{ - PayoutStatus: payoutStatus, - CustodianRegions: custodianRegions, - BATRate: rate, - AutoContribute: AutoContribute{ - DefaultChoice: defaultChoice, - Choices: choices, - }, - Tips: Tips{ - DefaultTipChoices: getTipChoices(ctx), - DefaultMonthlyChoices: getMonthlyChoices(ctx), - }, - } - - vbatDeadline, ok := ctx.Value(appctx.ParametersVBATDeadlineCTXKey).(time.Time) - if ok { - params.VBATDeadline = &vbatDeadline - } - - transition, ok := ctx.Value(appctx.ParametersTransitionCTXKey).(bool) - if ok { - params.Transition = transition - } - - return params, nil -} diff --git a/services/rewards/service_test.go b/services/rewards/service_test.go deleted file mode 100644 index 18eebe2ef..000000000 --- a/services/rewards/service_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package rewards - -import "testing" - -func TestGetParameters(t *testing.T) { - -} diff --git a/services/skus/Dockerfile b/services/skus/Dockerfile deleted file mode 100644 index 643298bb6..000000000 --- a/services/skus/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM bat-go:latest -CMD ["bat-go", "serve", "payment", "rest"] diff --git a/services/skus/README.md b/services/skus/README.md deleted file mode 100644 index 3110b7a9e..000000000 --- a/services/skus/README.md +++ /dev/null @@ -1,244 +0,0 @@ - -# Brave SKUs Service - -### Getting Started - -1. Begin with development setup steps 1 through 4 from the [bat-go readme](https://github.com/brave-intl/bat-go/blob/master/README.md) - -2. Bring up the SKUs containers locally with ```make docker-refresh-payment``` - -3. View container logs with ```docker logs grant-payment-refresh``` - -4. SKUs API will be available at localhost:3335 - -5. Commit code and refresh the containers with ```docker restart grant-payment-refresh```! - -### SKU Tokens - -SKU Tokens represent cookie-like objects with domain specific caveats, new tokens can be created following the instructions in [this readme](https://github.com/brave-intl/bat-go/tree/master/cmd#create-a-macaroon) - -This is an example of one possible [SKU token](https://github.com/brave-intl/bat-go/blob/brave-together-dev/cmd/macaroon/brave-together/brave_together_paid_dev.yaml): - -``` -tokens: - - id: "brave-together-paid sku token v1" - version: 1 - location: "together.bsg.brave.software" - first_party_caveats: - - sku: "brave-together-paid" - - price: 5 - - currency: "USD" - - description: "One month paid subscription for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] -``` - -### Creating a Free Trial - -Given the knowledge of a free trial SKU unlimited numbers of trials can be created. Care must be taken to keep free trial SKUs secret. - -Certain SKU tokens do not have a price, and once users have created an order for them they may redeem credentials to access the related service. In this example, we will create a free trial order for Brave Talk: - -Construct a `POST` request to ```/v1/orders``` with the following metadata - -``` -{ - "items": [{ - "sku": "MDAyOWxvY2F0aW9uIHRvZ2V0aGVyLmJzZy5icmF2ZS5zb2Z0d2FyZQowMDMwaWRlbnRpZmllciBicmF2ZS10b2dldGhlci1mcmVlIHNrdSB0b2tlbiB2MQowMDIwY2lkIHNrdT1icmF2ZS10b2dldGhlci1mcmVlCjAwMTBjaWQgcHJpY2U9MAowMDE1Y2lkIGN1cnJlbmN5PUJBVAowMDNjY2lkIGRlc2NyaXB0aW9uPU9uZSBtb250aCBmcmVlIHRyaWFsIGZvciBCcmF2ZSBUb2dldGhlcgowMDI1Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDJmc2lnbmF0dXJlIGebBXoPnj06tvlJkPEDLp9nfWo6Wfc1Txj6jTlgxjrQCg==", - "quantity": 1 - }] -} -``` - -Upon GET request to ```/v1/orders/:orderId```, server will respond with: - -``` -{ - "id": "92aafa4b-da7e-46b1-99c1-e8d30b26bc0f", - "createdAt": "2021-04-12T00:11:51.954386Z", - "currency": "BAT", - "updatedAt": "2021-04-12T00:11:51.954386Z", - "totalPrice": "0", - "merchantId": "brave.com", - "location": "together.bsg.brave.software", - "status": "paid", - "items": [{ - "id": "0b573a13-3c3d-40b7-bdac-879c531d31fb", - "orderId": "92aafa4b-da7e-46b1-99c1-e8d30b26bc0f", - "sku": "brave-together-free", - "createdAt": "2021-04-12T00:11:51.954386Z", - "updatedAt": "2021-04-12T00:11:51.954386Z", - "currency": "BAT", - "quantity": 1, - "price": "0", - "subtotal": "0", - "location": "together.bsg.brave.software", - "description": "One month free trial for Brave Together", - "type": "time-limited" - }] -} -``` - -Because the status on this order is _paid_ we may create and request credentials - -Construct a `POST` request to ```/v1/orders/:orderId/credentials``` with the following metadata - -``` -{ - "itemId": , - "blindedCreds": [] -} -``` - -Server will respond with status 200 OK. To retrieve these credentials construct a `GET` request to ```/v1/orders/:orderId/credentials``` - -Server will respond with the following payload: - -``` -[ - { - "id": "0b573a13-3c3d-40b7-bdac-879c531d31fb", - "orderId": "92aafa4b-da7e-46b1-99c1-e8d30b26bc0f", - "issuedAt": "2021-04-12", - "expiresAt": "2021-05-17", - "token": "ZCtG5A8lvArgJtBOR4I4tfHmDsM+pBrb9STaa1k1qbOhGHaYO2HFA2MUvoJ9edGX" - } -] -``` - -### Creating a Paid Order - -Similarly to a free order, we will submit a creation request, however we will include an email which will be needed for management of subscriptions - -Construct a `POST` request to ```/v1/orders``` with the following metadata - -``` -{"items": [ -{"sku": "MDAyOWxvY2F0aW9uIHRvZ2V0aGVyLmJzZy5icmF2ZS5zb2Z0d2FyZQowMDMwaWRlbnRpZmllciBicmF2ZS10b2dldGhlci1wYWlkIHNrdSB0b2tlbiB2MQowMDIwY2lkIHNrdT1icmF2ZS10b2dldGhlci1wYWlkCjAwMTBjaWQgcHJpY2U9NQowMDE1Y2lkIGN1cnJlbmN5PVVTRAowMDQzY2lkIGRlc2NyaXB0aW9uPU9uZSBtb250aCBwYWlkIHN1YnNjcmlwdGlvbiBmb3IgQnJhdmUgVG9nZXRoZXIKMDAyNWNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyZnNpZ25hdHVyZSDKLJ7NuuzP3KdmTdVnn0dI3JmIfNblQKmY+WBJOqnQJAo=", "quantity": 1}], - "email": "customeremail@gmail.com" -} -``` - -Upon `GET` request to ```/v1/orders/:orderId```, server will respond with: - -``` -{ - "id": "89ded3d9-55e1-4e26-bc18-228e76cf03ca", - "createdAt": "2021-04-12T00:51:44.61976Z", - "currency": "USD", - "updatedAt": "2021-04-12T00:51:45.515351Z", - "totalPrice": "5", - "merchantId": "brave.com", - "location": "together.bsg.brave.software", - "status": "pending", - "items": [{ - "id": "355ae321-0ceb-4698-9fed-158190da6fa4", - "orderId": "89ded3d9-55e1-4e26-bc18-228e76cf03ca", - "sku": "brave-together-paid", - "createdAt": "2021-04-12T00:51:44.61976Z", - "updatedAt": "2021-04-12T00:51:44.61976Z", - "currency": "USD", - "quantity": 1, - "price": "5", - "subtotal": "5", - "location": "together.bsg.brave.software", - "description": "One month paid subscription for Brave Together", - "type": "time-limited" - }], - "stripeCheckoutSessionId": "cs_test_a1g0n8FWNT3ClB2p9shKlpchGTw7cDKQCOqLJ1dSBcRd9ZsLssrtDLbZgM" -} -``` - -Because this order is Stripe payable, we will receive a Stripe Checkout Session Id, this identifier can be used to fulfill the order via a credit card. For example if we construct the following sample script: - -```html - - - - Buy cool new product - - - - - - - - - -``` - -And host locally, we can redirect to a page similar to this: - -Screen Shot 2021-04-11 at 9 18 10 PM - -A credit card can then be entered, and upon successful payment, the corresponding order will become paid. Once paid, credentials may be created and requested as in the free trial. - -### Architecture - -Documentation above refers to a general use of the payment service. In the context of Brave Talk, the order of various calls and the service making the call is outlined in the diagram below: - -Screen Shot 2021-04-30 at 1 58 22 PM - - -### Stripe Integration - -For the stripe integration details refer to the diagram below with numbered interactions. - -[stripe integration diagram](docs/StripeSKUsIntegration.pdf) - -### Submitting a Receipt - -In some circumstances it is desirable to submit a receipt for a particular order to prove it was -paid, and have the skus service handle the validation of said receipt. Currently implemented -there are two receipt providers of which SKUs is capable of validating payment was collected, -android and ios. - -``` -curl -XPOST /v1/orders//submit-receipt -d '' -``` - -The payload of the above call is a Base64 encoded string of a JSON document. Two examples follow: - -```json -{ - "type": "ios", - "raw_receipt": "", - "package": "com.brave...", // android specific, - "subscription_id": "brave-firewall-vpn-premium", // the sku string value of the subscription -} -``` - -The and example POST payload of the API call to submit receipt is the above json base64 encoded. -### Submitting a Receipt - -In some circumstances it is desirable to submit a receipt for a particular order to prove it was -paid, and have the skus service handle the validation of said receipt. Currently implemented -there are two receipt providers of which SKUs is capable of validating payment was collected, -android and ios. - -``` -curl -XPOST /v1/orders//submit-receipt -d '' -``` - -The payload of the above call is a Base64 encoded string of a JSON document. Two examples follow: - -```json -{ - "type": "ios", - "raw_receipt": "", - "package": "com.brave...", // android specific, - "subscription_id": "brave-firewall-vpn-premium", // the sku string value of the subscription -} -``` - -The and example POST payload of the API call to submit receipt is the above json base64 encoded. diff --git a/services/skus/avro.go b/services/skus/avro.go deleted file mode 100644 index 58a23054d..000000000 --- a/services/skus/avro.go +++ /dev/null @@ -1,242 +0,0 @@ -package skus - -import ( - "encoding/json" - "fmt" - - "github.com/brave-intl/bat-go/libs/ptr" - uuid "github.com/satori/go.uuid" -) - -const voteSchema = `{ - "namespace": "brave.payments", - "type": "record", - "name": "vote", - "doc": "This message is sent when a user funded wallet has successfully auto-contributed to a channel", - "fields": [ - { "name": "id", "type": "string" }, - { "name": "type", "type": "string" }, - { "name": "channel", "type": "string" }, - { "name": "createdAt", "type": "string" }, - { "name": "baseVoteValue", "type": "string", "default":"0.25" }, - { "name": "voteTally", "type": "long", "default":1 }, - { "name": "fundingSource", "type": "string", "default": "uphold" } - ] -}` - -const signingOrderRequestSchema = `{ - "namespace": "brave.payments", - "type": "record", - "doc": "Top level request containing the data to be processed, as well as any top level metadata for this message.", - "name": "signingOrderRequestSchema", - "fields" : [ - {"name": "request_id", "type": "string"}, - { - "name": "data", - "type": { - "type": "array", - "items": { - "namespace": "brave.payments", - "type": "record", - "name": "SigningOrder", - "fields": [ - {"name": "associated_data", "type": "bytes", "doc": "contains METADATA"}, - { - "name": "blinded_tokens", - "type": { - "type": "array", - "items": { - "name": "blinded_token", - "type": "string", - "namespace": "brave.payments" - } - } - }, - {"name": "issuer_type", "type": "string"}, - {"name": "issuer_cohort", "type": "int"} - ] - } - } - } - ] -}` - -// SigningOrderRequest - the structure of a signing order request -type SigningOrderRequest struct { - RequestID string `json:"request_id"` - Data []SigningOrder `json:"data"` -} - -// SigningOrder - signing order structure -type SigningOrder struct { - AssociatedData []byte `json:"associated_data"` - BlindedTokens []string `json:"blinded_tokens"` - IssuerType string `json:"issuer_type"` - IssuerCohort int16 `json:"issuer_cohort"` -} - -// Metadata - skus metadata structure -type Metadata struct { - ItemID uuid.UUID `json:"itemId"` - OrderID uuid.UUID `json:"orderId"` - IssuerID uuid.UUID `json:"issuerId"` - CredentialType string `json:"credential_type"` -} - -const signingOrderResultSchema = `{ - "namespace": "brave.payments", - "type": "record", - "doc": "Top level request containing the data to be processed, as well as any top level metadata for this message.", - "name": "signingOrderResultSchema", - "fields" : [ - {"name": "request_id", "type": "string"}, - { - "name": "data", - "type": { - "type": "array", - "items":{ - "namespace": "brave.payments", - "type": "record", - "name": "SignedOrder", - "fields": [ - { - "name": "signed_tokens", - "type": { - "type": "array", - "items": { - "name": "signed_token", - "type": "string" - } - } - }, - {"name": "public_key", "type": "string"}, - {"name": "proof", "type": "string"}, - {"name": "status", "type": { - "name": "SigningResultStatus", - "type": "enum", - "symbols": ["ok", "invalid_issuer", "error"] - }}, - {"name": "associated_data", "type": "bytes", "doc": "contains METADATA"}, - {"name": "valid_to", "type": ["null", "string"], "default": null}, - {"name": "valid_from", "type": ["null", "string"], "default": null}, - { - "name": "blinded_tokens", - "type": {"type" : "array", "items": {"type": "string"}}, - "default": [] - } - ] - } - } - } - ] -}` - -// SigningOrderResult - structure of a signing result -type SigningOrderResult struct { - RequestID string `json:"request_id"` - Data []SignedOrder `json:"data"` -} - -// SignedOrder - structure for a signed order -type SignedOrder struct { - PublicKey string `json:"public_key"` - Proof string `json:"proof"` - Status SignedOrderStatus `json:"status"` - SignedTokens []string `json:"signed_tokens"` - BlindedTokens []string `json:"blinded_tokens"` - ValidTo *UnionNullString `json:"valid_to"` - ValidFrom *UnionNullString `json:"valid_from"` - AssociatedData []byte `json:"associated_data"` -} - -// SignedOrderStatus - signed order status structure -type SignedOrderStatus int - -const ( - // SignedOrderStatusOk - Okay status from signed order status - SignedOrderStatusOk SignedOrderStatus = iota - // SignedOrderStatusInvalidIssuer - invalid issuer - SignedOrderStatusInvalidIssuer - // SignedOrderStatusError - error status for signed order status - SignedOrderStatusError -) - -// MarshalJSON - marshaller for signed order status -func (s SignedOrderStatus) MarshalJSON() ([]byte, error) { - var status string - switch s { - case SignedOrderStatusOk: - status = "ok" - case SignedOrderStatusInvalidIssuer: - status = "invalid_issuer" - case SignedOrderStatusError: - status = "error" - default: - return nil, fmt.Errorf("signed order creds marshal error: invalid type %s", status) - } - - return json.Marshal(status) -} - -// UnmarshalJSON - unmarshaller for signed order status -func (s *SignedOrderStatus) UnmarshalJSON(data []byte) error { - var str string - err := json.Unmarshal(data, &str) - if err != nil { - return fmt.Errorf("signed order creds unmarshal error: %w", err) - } - - switch str { - case "ok": - *s = SignedOrderStatusOk - case "invalid_issuer": - *s = SignedOrderStatusInvalidIssuer - case "error": - *s = SignedOrderStatusError - default: - return fmt.Errorf("signed order creds unmarshal error: invalid type %s", str) - } - - return nil -} - -// String - stringer for signed order status -func (s SignedOrderStatus) String() string { - switch s { - case SignedOrderStatusOk: - return "ok" - case SignedOrderStatusInvalidIssuer: - return "invalid_issuer" - case SignedOrderStatusError: - return "error" - default: - return fmt.Sprintf("%d", int(s)) - } -} - -// UnionNullString - type describing -type UnionNullString map[string]interface{} - -// UnmarshalJSON - implement unmarshaling for union null string -func (u *UnionNullString) UnmarshalJSON(data []byte) error { - var temp map[string]interface{} - err := json.Unmarshal(data, &temp) - if err != nil { - return fmt.Errorf("error deserializing union: %w", err) - } - *u = temp - return nil -} - -// Value - perform a valuer on unionnullstring -func (u UnionNullString) Value() *string { - s, ok := u["string"] - if ok { - return ptr.FromString(s.(string)) - } - _, ok = u["null"] - if ok { - return nil - } - panic("unknown value for union") -} diff --git a/services/skus/controllers.go b/services/skus/controllers.go deleted file mode 100644 index c1642d835..000000000 --- a/services/skus/controllers.go +++ /dev/null @@ -1,1371 +0,0 @@ -package skus - -import ( - "context" - "crypto/subtle" - "encoding/json" - "errors" - "fmt" - "net/http" - "os" - "strconv" - - "github.com/asaskevich/govalidator" - "github.com/go-chi/chi" - "github.com/go-chi/cors" - uuid "github.com/satori/go.uuid" - stripe "github.com/stripe/stripe-go/v72" - "github.com/stripe/stripe-go/v72/webhook" - - "github.com/brave-intl/bat-go/libs/clients/radom" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/brave-intl/bat-go/libs/responses" - "github.com/brave-intl/bat-go/services/skus/handler" - "github.com/brave-intl/bat-go/services/skus/model" -) - -type middlewareFn func(next http.Handler) http.Handler - -func Router( - svc *Service, - authMwr middlewareFn, - metricsMwr middleware.InstrumentHandlerDef, - copts cors.Options, -) chi.Router { - r := chi.NewRouter() - - orderh := handler.NewOrder(svc) - - corsMwrPost := NewCORSMwr(copts, http.MethodPost) - - if os.Getenv("ENV") == "local" { - r.Method( - http.MethodOptions, - "/", - metricsMwr("CreateOrderOptions", corsMwrPost(nil)), - ) - - r.Method( - http.MethodPost, - "/", - metricsMwr( - "CreateOrder", - corsMwrPost(handlers.AppHandler(orderh.Create)), - ), - ) - } else { - r.Method(http.MethodPost, "/", metricsMwr("CreateOrder", handlers.AppHandler(orderh.Create))) - } - - { - corsMwrGet := NewCORSMwr(copts, http.MethodGet) - r.Method(http.MethodOptions, "/{orderID}", metricsMwr("GetOrderOptions", corsMwrGet(nil))) - r.Method(http.MethodGet, "/{orderID}", metricsMwr("GetOrder", corsMwrGet(GetOrder(svc)))) - } - - r.Method( - http.MethodDelete, - "/{orderID}", - metricsMwr("CancelOrder", NewCORSMwr(copts, http.MethodDelete)(authMwr(CancelOrder(svc)))), - ) - - r.Method( - http.MethodPatch, - "/{orderID}/set-trial", - metricsMwr("SetOrderTrialDays", NewCORSMwr(copts, http.MethodPatch)(authMwr(SetOrderTrialDays(svc)))), - ) - - r.Method(http.MethodGet, "/{orderID}/transactions", metricsMwr("GetTransactions", GetTransactions(svc))) - r.Method(http.MethodPost, "/{orderID}/transactions/uphold", metricsMwr("CreateUpholdTransaction", CreateUpholdTransaction(svc))) - r.Method(http.MethodPost, "/{orderID}/transactions/gemini", metricsMwr("CreateGeminiTransaction", CreateGeminiTransaction(svc))) - - r.Method( - http.MethodPost, - "/{orderID}/transactions/anonymousCard", - metricsMwr("CreateAnonCardTransaction", CreateAnonCardTransaction(svc)), - ) - - // Receipt validation. - r.Method(http.MethodPost, "/{orderID}/submit-receipt", metricsMwr("SubmitReceipt", corsMwrPost(SubmitReceipt(svc)))) - - r.Route("/{orderID}/credentials", func(cr chi.Router) { - cr.Use(NewCORSMwr(copts, http.MethodGet, http.MethodPost)) - cr.Method(http.MethodGet, "/", metricsMwr("GetOrderCreds", GetOrderCreds(svc))) - cr.Method(http.MethodPost, "/", metricsMwr("CreateOrderCreds", CreateOrderCreds(svc))) - cr.Method(http.MethodDelete, "/", metricsMwr("DeleteOrderCreds", authMwr(DeleteOrderCreds(svc)))) - - // Handle the old endpoint while the new is being rolled out: - // - true: the handler uses itemID as the request id, which is the old mode; - // - false: the handler uses the requestID from the URI. - cr.Method(http.MethodGet, "/{itemID}", metricsMwr("GetOrderCredsByID", getOrderCredsByID(svc, true))) - cr.Method(http.MethodGet, "/items/{itemID}/batches/{requestID}", metricsMwr("GetOrderCredsByID", getOrderCredsByID(svc, false))) - - cr.Method(http.MethodPut, "/items/{itemID}/batches/{requestID}", metricsMwr("CreateOrderItemCreds", createItemCreds(svc))) - }) - - return r -} - -// CredentialRouter handles requests to /v1/credentials. -func CredentialRouter(svc *Service, authMwr middlewareFn) chi.Router { - r := chi.NewRouter() - - r.Method( - http.MethodPost, - "/subscription/verifications", - middleware.InstrumentHandler("VerifyCredentialV1", authMwr(VerifyCredentialV1(svc))), - ) - - return r -} - -// CredentialV2Router handles requests to /v2/credentials. -func CredentialV2Router(svc *Service, authMwr middlewareFn) chi.Router { - r := chi.NewRouter() - - r.Method( - http.MethodPost, - "/subscription/verifications", - middleware.InstrumentHandler("VerifyCredentialV2", authMwr(VerifyCredentialV2(svc))), - ) - - return r -} - -// MerchantRouter handles calls made for the merchant -func MerchantRouter(service *Service) chi.Router { - r := chi.NewRouter() - if os.Getenv("ENV") != "local" { - r.Use(middleware.SimpleTokenAuthorizedOnly) - } - - // Once instrument handler is refactored https://github.com/brave-intl/bat-go/issues/291 - // We can use this service context instead of having - r.Use(middleware.NewServiceCtx(service)) - - // RESTy routes for "merchant" resource - r.Route("/", func(r chi.Router) { - r.Route("/{merchantID}", func(mr chi.Router) { - mr.Route("/keys", func(kr chi.Router) { - kr.Method("GET", "/", middleware.InstrumentHandler("GetKeys", GetKeys(service))) - kr.Method("POST", "/", middleware.InstrumentHandler("CreateKey", CreateKey(service))) - kr.Method("DELETE", "/{id}", middleware.InstrumentHandler("DeleteKey", DeleteKey(service))) - }) - mr.Route("/transactions", func(kr chi.Router) { - kr.Method("GET", "/", middleware.InstrumentHandler("MerchantTransactions", MerchantTransactions(service))) - }) - }) - }) - - return r -} - -// DeleteKeyRequest includes information needed to delete a key -type DeleteKeyRequest struct { - DelaySeconds int `json:"delaySeconds" valid:"-"` -} - -// CreateKeyRequest includes information needed to create a key -type CreateKeyRequest struct { - Name string `json:"name" valid:"required"` -} - -// CreateKeyResponse includes information about the created key -type CreateKeyResponse struct { - *Key - SecretKey string `json:"secretKey"` -} - -// CreateKey is the handler for creating keys for a merchant -func CreateKey(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - reqMerchant := chi.URLParam(r, "merchantID") - - var req CreateKeyRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - encrypted, nonce, err := GenerateSecret() - if err != nil { - return handlers.WrapError(err, "Could not generate a secret key ", http.StatusInternalServerError) - } - - key, err := service.Datastore.CreateKey(reqMerchant, req.Name, encrypted, nonce) - if err != nil { - return handlers.WrapError(err, "Error create api keys", http.StatusInternalServerError) - } - - sk, err := key.GetSecretKey() - if err != nil { - return handlers.WrapError(err, "Error create api keys", http.StatusInternalServerError) - } - - if sk == nil { - err = errors.New("secret key was nil") - return handlers.WrapError(err, "Error create api keys", http.StatusInternalServerError) - } - - resp := CreateKeyResponse{ - Key: key, - SecretKey: *sk, - } - return handlers.RenderContent(r.Context(), resp, w, http.StatusOK) - }) -} - -// DeleteKey deletes a key -func DeleteKey(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var id = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), id, chi.URLParam(r, "id")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "id": err.Error(), - }, - ) - } - - var req DeleteKeyRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - key, err := service.Datastore.DeleteKey(*id.UUID(), req.DelaySeconds) - if err != nil { - return handlers.WrapError(err, "Error updating keys for the merchant", http.StatusInternalServerError) - } - status := http.StatusOK - if key == nil { - status = http.StatusNotFound - } - - return handlers.RenderContent(r.Context(), key, w, status) - }) -} - -// GetKeys returns all keys for a specified merchant -func GetKeys(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - merchantID := chi.URLParam(r, "merchantID") - expired := r.URL.Query().Get("expired") - showExpired := expired == "true" - - var keys *[]Key - keys, err := service.Datastore.GetKeysByMerchant(merchantID, showExpired) - if err != nil { - return handlers.WrapError(err, "Error Getting Keys for Merchant", http.StatusInternalServerError) - } - - return handlers.RenderContent(r.Context(), keys, w, http.StatusOK) - }) -} - -// VoteRouter for voting endpoint -func VoteRouter(service *Service, instrumentHandler middleware.InstrumentHandlerDef) chi.Router { - r := chi.NewRouter() - r.Method("POST", "/", instrumentHandler("MakeVote", MakeVote(service))) - return r -} - -type setTrialDaysRequest struct { - TrialDays int64 `json:"trialDays" valid:"int"` -} - -// SetOrderTrialDays handles requests for setting trial days on orders. -func SetOrderTrialDays(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - orderID := &inputs.ID{} - - if err := inputs.DecodeAndValidateString(ctx, orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{"orderID": err.Error()}, - ) - } - - // validate order merchant and caveats (to make sure this is the right merch) - if err := service.validateOrderMerchantAndCaveats(ctx, *orderID.UUID()); err != nil { - return handlers.ValidationError( - "Error validating request merchant and caveats", - map[string]interface{}{"orderMerchantAndCaveats": err.Error()}, - ) - } - - req := &setTrialDaysRequest{} - if err := requestutils.ReadJSON(ctx, r.Body, req); err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - if _, err := govalidator.ValidateStruct(req); err != nil { - return handlers.WrapValidationError(err) - } - - if err := service.SetOrderTrialDays(ctx, orderID.UUID(), req.TrialDays); err != nil { - return handlers.WrapError(err, "Error setting the trial days on the order", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, nil, w, http.StatusOK) - }) -} - -// CancelOrder handles requests for cancelling orders. -func CancelOrder(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - orderID := &inputs.ID{} - - if err := inputs.DecodeAndValidateString(ctx, orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{"orderID": err.Error()}, - ) - } - - oid := *orderID.UUID() - - if err := service.validateOrderMerchantAndCaveats(ctx, oid); err != nil { - return handlers.WrapError(err, "Error validating auth merchant and caveats", http.StatusForbidden) - } - - if err := service.CancelOrder(oid); err != nil { - return handlers.WrapError(err, "Error retrieving the order", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, nil, w, http.StatusOK) - }) -} - -// GetOrder is the handler for getting an order -func GetOrder(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var orderID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - order, err := service.GetOrder(*orderID.UUID()) - if err != nil { - return handlers.WrapError(err, "Error retrieving the order", http.StatusInternalServerError) - } - - status := http.StatusOK - if order == nil { - status = http.StatusNotFound - } - - return handlers.RenderContent(r.Context(), order, w, status) - }) -} - -// GetTransactions is the handler for listing the transactions for an order -func GetTransactions(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var orderID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - transactions, err := service.Datastore.GetTransactions(*orderID.UUID()) - if err != nil { - return handlers.WrapError(err, "Error retrieving the transactions", http.StatusInternalServerError) - } - - return handlers.RenderContent(r.Context(), transactions, w, http.StatusOK) - }) -} - -// CreateTransactionRequest includes information needed to create a transaction -type CreateTransactionRequest struct { - ExternalTransactionID string `json:"externalTransactionId" valid:"required,uuid"` -} - -// CreateGeminiTransaction creates a transaction against an order -func CreateGeminiTransaction(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req CreateTransactionRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - var orderID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - // Ensure the external transaction ID hasn't already been added to any orders. - transaction, err := service.Datastore.GetTransaction(req.ExternalTransactionID) - if err != nil { - return handlers.WrapError(err, "externalTransactinID has already been submitted to an order", http.StatusConflict) - } - - if transaction != nil { - // if the transaction is already added, then do an update - transaction, err = service.UpdateTransactionFromRequest(r.Context(), req, *orderID.UUID(), service.getGeminiCustodialTx) - if err != nil { - return handlers.WrapError(err, "Error updating the transaction", http.StatusBadRequest) - } - // return 200 in event of already created transaction - return handlers.RenderContent(r.Context(), transaction, w, http.StatusOK) - } - - transaction, err = service.CreateTransactionFromRequest(r.Context(), req, *orderID.UUID(), service.getGeminiCustodialTx) - if err != nil { - return handlers.WrapError(err, "Error creating the transaction", http.StatusBadRequest) - } - - return handlers.RenderContent(r.Context(), transaction, w, http.StatusCreated) - }) -} - -// CreateUpholdTransaction creates a transaction against an order -func CreateUpholdTransaction(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var req CreateTransactionRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - var orderID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - // Ensure the external transaction ID hasn't already been added to any orders. - transaction, err := service.Datastore.GetTransaction(req.ExternalTransactionID) - if err != nil { - return handlers.WrapError(err, "externalTransactinID has already been submitted to an order", http.StatusConflict) - } - - if transaction != nil { - // if the transaction is already added, then do an update - transaction, err = service.UpdateTransactionFromRequest(r.Context(), req, *orderID.UUID(), getUpholdCustodialTxWithRetries) - if err != nil { - return handlers.WrapError(err, "Error updating the transaction", http.StatusBadRequest) - } - // return 200 in event of already created transaction - return handlers.RenderContent(r.Context(), transaction, w, http.StatusOK) - } - - transaction, err = service.CreateTransactionFromRequest(r.Context(), req, *orderID.UUID(), getUpholdCustodialTxWithRetries) - if err != nil { - return handlers.WrapError(err, "Error creating the transaction", http.StatusBadRequest) - } - - return handlers.RenderContent(r.Context(), transaction, w, http.StatusCreated) - }) -} - -// CreateAnonCardTransactionRequest includes information needed to create a anon card transaction -type CreateAnonCardTransactionRequest struct { - WalletID uuid.UUID `json:"paymentId"` - Transaction string `json:"transaction"` -} - -// CreateAnonCardTransaction creates a transaction against an order -func CreateAnonCardTransaction(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - sublogger := logging.Logger(ctx, "payments").With(). - Str("func", "CreateAnonCardTransaction"). - Logger() - var req CreateAnonCardTransactionRequest - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - var orderID = new(inputs.ID) - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - transaction, err := service.CreateAnonCardTransaction(r.Context(), req.WalletID, req.Transaction, *orderID.UUID()) - if err != nil { - sublogger.Error().Err(err).Msg("failed to create anon card transaction") - return handlers.WrapError(err, "Error creating anon card transaction", http.StatusInternalServerError) - } - - return handlers.RenderContent(r.Context(), transaction, w, http.StatusCreated) - }) -} - -// CreateOrderCredsRequest includes the item ID and blinded credentials which to be signed. -type CreateOrderCredsRequest struct { - ItemID uuid.UUID `json:"itemId" valid:"-"` - BlindedCreds []string `json:"blindedCreds" valid:"base64"` -} - -// CreateOrderCreds handles requests for creating credentials. -func CreateOrderCreds(svc *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - lg := logging.Logger(ctx, "skus.CreateOrderCreds") - - req := &CreateOrderCredsRequest{} - if err := requestutils.ReadJSON(ctx, r.Body, req); err != nil { - lg.Error().Err(err).Msg("failed to read body payload") - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - if _, err := govalidator.ValidateStruct(req); err != nil { - lg.Error().Err(err).Msg("failed to validate struct") - return handlers.WrapValidationError(err) - } - - orderID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, orderID, chi.URLParam(r, "orderID")); err != nil { - lg.Error().Err(err).Msg("failed to validate order id") - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - // Use the itemID for the request id so the old credential uniqueness constraint remains enforced. - reqID := req.ItemID - - if err := svc.CreateOrderItemCredentials(ctx, *orderID.UUID(), req.ItemID, reqID, req.BlindedCreds); err != nil { - lg.Error().Err(err).Msg("failed to create the order credentials") - return handlers.WrapError(err, "Error creating order creds", http.StatusBadRequest) - } - - return handlers.RenderContent(ctx, nil, w, http.StatusOK) - } -} - -// createItemCredsRequest includes the blinded credentials to be signed. -type createItemCredsRequest struct { - BlindedCreds []string `json:"blindedCreds" valid:"base64"` -} - -// createItemCreds handles requests for creating credentials for an item. -func createItemCreds(svc *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - lg := logging.Logger(ctx, "skus.createItemCreds") - - req := &createItemCredsRequest{} - if err := requestutils.ReadJSON(ctx, r.Body, req); err != nil { - lg.Error().Err(err).Msg("failed to read body payload") - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - if _, err := govalidator.ValidateStruct(req); err != nil { - lg.Error().Err(err).Msg("failed to validate struct") - return handlers.WrapValidationError(err) - } - - orderID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, orderID, chi.URLParamFromCtx(ctx, "orderID")); err != nil { - lg.Error().Err(err).Msg("failed to validate order id") - return handlers.ValidationError("Error validating request url parameter", map[string]interface{}{ - "orderID": err.Error(), - }) - } - - itemID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, itemID, chi.URLParamFromCtx(ctx, "itemID")); err != nil { - lg.Error().Err(err).Msg("failed to validate item id") - return handlers.ValidationError("Error validating request url parameter", map[string]interface{}{ - "itemID": err.Error(), - }) - } - - reqID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, reqID, chi.URLParamFromCtx(ctx, "requestID")); err != nil { - lg.Error().Err(err).Msg("failed to validate request id") - return handlers.ValidationError("Error validating request url parameter", map[string]interface{}{ - "requestID": err.Error(), - }) - } - - if err := svc.CreateOrderItemCredentials(ctx, *orderID.UUID(), *itemID.UUID(), *reqID.UUID(), req.BlindedCreds); err != nil { - lg.Error().Err(err).Msg("failed to create the order credentials") - return handlers.WrapError(err, "Error creating order creds", http.StatusBadRequest) - } - - return handlers.RenderContent(ctx, nil, w, http.StatusOK) - } -} - -// GetOrderCreds is the handler for fetching all order credentials associated with an order. -// This endpoint handles the retrieval of all order credential types i.e. single-use, time-limited and time-limited-v2. -func GetOrderCreds(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var orderID = new(inputs.ID) - - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{ - "orderID": err.Error(), - }, - ) - } - - creds, status, err := service.GetCredentials(r.Context(), *orderID.UUID()) - if err != nil { - if errors.Is(err, errSetRetryAfter) { - // error specifies a retry after period, add to response header - avg, err := service.Datastore.GetOutboxMovAvgDurationSeconds() - if err != nil { - return handlers.WrapError(err, "Error getting credential retry-after", status) - } - w.Header().Set("Retry-After", strconv.FormatInt(avg, 10)) - } else { - return handlers.WrapError(err, "Error getting credentials", status) - } - } - return handlers.RenderContent(r.Context(), creds, w, status) - } -} - -// DeleteOrderCreds handles requests for deleting order credentials. -func DeleteOrderCreds(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - orderID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, orderID, chi.URLParam(r, "orderID")); err != nil { - return handlers.ValidationError( - "Error validating request url parameter", - map[string]interface{}{"orderID": err.Error()}, - ) - } - - id := *orderID.UUID() - if err := service.validateOrderMerchantAndCaveats(ctx, id); err != nil { - return handlers.WrapError(err, "Error validating auth merchant and caveats", http.StatusForbidden) - } - - isSigned := r.URL.Query().Get("isSigned") == "true" - if err := service.DeleteOrderCreds(ctx, id, isSigned); err != nil { - return handlers.WrapError(err, "Error deleting credentials", http.StatusBadRequest) - } - - return handlers.RenderContent(ctx, "Order credentials successfully deleted", w, http.StatusOK) - } -} - -// getOrderCredsByID handles requests for fetching order credentials by an item id. -// -// Requests may come in via two endpoints: -// - /{itemID} – legacyMode, reqID == itemID -// - /items/{itemID}/batches/{requestID} – new mode, reqID == requestID. -// -// The legacy mode will be gone after confirming a successful rollout. -// -// TODO: Clean up the legacy mode. -func getOrderCredsByID(svc *Service, legacyMode bool) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - - orderID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, orderID, chi.URLParamFromCtx(ctx, "orderID")); err != nil { - return handlers.ValidationError("Error validating request url parameter", map[string]interface{}{ - "orderID": err.Error(), - }) - } - - itemID := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, itemID, chi.URLParamFromCtx(ctx, "itemID")); err != nil { - return handlers.ValidationError("Error validating request url parameter", map[string]interface{}{ - "itemID": err.Error(), - }) - } - - var reqID uuid.UUID - if legacyMode { - reqID = *itemID.UUID() - } else { - reqIDRaw := &inputs.ID{} - if err := inputs.DecodeAndValidateString(ctx, reqIDRaw, chi.URLParamFromCtx(ctx, "requestID")); err != nil { - return handlers.ValidationError("Error validating request url parameter", map[string]interface{}{ - "requestID": err.Error(), - }) - } - - reqID = *reqIDRaw.UUID() - } - - creds, status, err := svc.GetItemCredentials(ctx, *orderID.UUID(), *itemID.UUID(), reqID) - if err != nil { - if !errors.Is(err, errSetRetryAfter) { - return handlers.WrapError(err, "Error getting credentials", status) - } - - // Add to response header as error specifies a retry after period. - avg, err := svc.Datastore.GetOutboxMovAvgDurationSeconds() - if err != nil { - return handlers.WrapError(err, "Error getting credential retry-after", status) - } - - w.Header().Set("Retry-After", strconv.FormatInt(avg, 10)) - } - - if creds == nil { - return handlers.RenderContent(ctx, map[string]interface{}{}, w, status) - } - - return handlers.RenderContent(ctx, creds, w, status) - }) -} - -// VoteRequest includes a suggestion payload and credentials to be redeemed -type VoteRequest struct { - Vote string `json:"vote" valid:"base64"` - Credentials []CredentialBinding `json:"credentials"` -} - -// MakeVote is the handler for making a vote using credentials -func MakeVote(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - req VoteRequest - ctx = r.Context() - ) - err := requestutils.ReadJSON(ctx, r.Body, &req) - if err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - logger := logging.Logger(ctx, "skus.MakeVote") - - _, err = govalidator.ValidateStruct(req) - if err != nil { - return handlers.WrapValidationError(err) - } - - err = service.Vote(ctx, req.Credentials, req.Vote) - if err != nil { - switch err.(type) { - case govalidator.Error: - logger.Warn().Err(err).Msg("failed vote validation") - return handlers.WrapValidationError(err) - case govalidator.Errors: - logger.Warn().Err(err).Msg("failed multiple vote validation") - return handlers.WrapValidationError(err) - default: - // check for custom vote invalidations - if errors.Is(err, ErrInvalidSKUToken) { - verr := handlers.ValidationError("failed to validate sku token", nil) - data := []string{} - if errors.Is(err, ErrInvalidSKUTokenSKU) { - data = append(data, "invalid sku value") - } - if errors.Is(err, ErrInvalidSKUTokenBadMerchant) { - data = append(data, "invalid merchant value") - } - verr.Data = data - logger.Warn().Err(err).Msg("failed sku validations") - return verr - } - logger.Warn().Err(err).Msg("failed to perform vote") - return handlers.WrapError(err, "Error making vote", http.StatusBadRequest) - } - } - - w.WriteHeader(http.StatusOK) - return nil - }) -} - -// MerchantTransactions is the handler for getting paginated merchant transactions -func MerchantTransactions(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - // inputs - // /merchants/{merchantID}/transactions?page=1&items=50&order=id - var ( - merchantID, mIDErr = inputs.NewMerchantID(r.Context(), chi.URLParam(r, "merchantID")) - ctx, pagination, pIDErr = inputs.NewPagination(r.Context(), r.URL.String(), new(Transaction)) - ) - - // Check Validation Errors - if mIDErr != nil { - return handlers.WrapValidationError(mIDErr) - } - if pIDErr != nil { - return handlers.WrapValidationError(pIDErr) - } - - // Get Paginated Results - transactions, total, err := service.Datastore.GetPagedMerchantTransactions( - ctx, merchantID.UUID(), pagination) - if err != nil { - return handlers.WrapError(err, "error getting transactions", http.StatusInternalServerError) - } - - // Build Response - response := &responses.PaginationResponse{ - Page: pagination.Page, - Items: pagination.Items, - MaxPage: total/pagination.Items - 1, // 0 indexed - Ordered: pagination.RawOrder, - Data: transactions, - } - - // render response - if err := response.Render(ctx, w, http.StatusOK); err != nil { - return handlers.WrapError(err, "error rendering response", http.StatusInternalServerError) - } - - return nil - }) -} - -// VerifyCredentialV2 - version 2 of verify credential -func VerifyCredentialV2(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - - ctx := r.Context() - l := logging.Logger(ctx, "VerifyCredentialV2") - - var req = new(VerifyCredentialRequestV2) - if err := inputs.DecodeAndValidateReader(ctx, req, r.Body); err != nil { - l.Error().Err(err).Msg("failed to read request") - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - appErr := service.verifyCredential(ctx, req, w) - if appErr != nil { - l.Error().Err(appErr).Msg("failed to verify credential") - } - - return appErr - } -} - -// VerifyCredentialV1 is the handler for verifying subscription credentials -func VerifyCredentialV1(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - l := logging.Logger(r.Context(), "VerifyCredentialV1") - - var req = new(VerifyCredentialRequestV1) - - err := requestutils.ReadJSON(r.Context(), r.Body, &req) - if err != nil { - l.Error().Err(err).Msg("failed to read request") - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - l.Debug().Msg("read verify credential post body") - - _, err = govalidator.ValidateStruct(req) - if err != nil { - l.Error().Err(err).Msg("failed to validate request") - return handlers.WrapError(err, "Error in request validation", http.StatusBadRequest) - } - - appErr := service.verifyCredential(ctx, req, w) - if appErr != nil { - l.Error().Err(appErr).Msg("failed to verify credential") - } - - return appErr - } -} - -// WebhookRouter - handles calls from various payment method webhooks informing payments of completion -func WebhookRouter(service *Service) chi.Router { - r := chi.NewRouter() - r.Method("POST", "/stripe", middleware.InstrumentHandler("HandleStripeWebhook", HandleStripeWebhook(service))) - r.Method("POST", "/radom", middleware.InstrumentHandler("HandleRadomWebhook", HandleRadomWebhook(service))) - r.Method("POST", "/android", middleware.InstrumentHandler("HandleAndroidWebhook", HandleAndroidWebhook(service))) - r.Method("POST", "/ios", middleware.InstrumentHandler("HandleIOSWebhook", HandleIOSWebhook(service))) - return r -} - -// HandleAndroidWebhook is the handler for the Google Playstore webhooks -func HandleAndroidWebhook(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - - var ( - ctx = r.Context() - req = new(AndroidNotification) - validationErrMap = map[string]interface{}{} // for tracking our validation errors - ) - - // get logger - logger := logging.Logger(ctx, "payments").With(). - Str("func", "HandleAndroidWebhook"). - Logger() - - // read the payload - payload, err := requestutils.Read(r.Context(), r.Body) - if err != nil { - logger.Error().Err(err).Msg("failed to read the payload") - return handlers.WrapValidationError(err) - } - - // validate the payload - if err := inputs.DecodeAndValidate(context.Background(), req, payload); err != nil { - logger.Debug().Str("payload", string(payload)). - Msg("failed to decode and validate the payload") - validationErrMap["request-body-decode"] = err.Error() - } - - // extract out the Developer notification - dn, err := req.Message.GetDeveloperNotification() - if err != nil { - validationErrMap["invalid-developer-notification"] = err.Error() - } - - if dn == nil || dn.SubscriptionNotification.PurchaseToken == "" { - logger.Error().Interface("validation-errors", validationErrMap). - Msg("failed to get developer notification from message") - validationErrMap["invalid-developer-notification-token"] = "notification has no purchase token" - } - - // if we had any validation errors, return the validation error map to the caller - if len(validationErrMap) != 0 { - return handlers.ValidationError("Error validating request url", validationErrMap) - } - - err = service.verifyDeveloperNotification(ctx, dn) - if err != nil { - logger.Error().Err(err).Msg("failed to verify subscription notification") - switch { - case errors.Is(err, errNotFound): - return handlers.WrapError(err, "failed to verify subscription notification", - http.StatusNotFound) - default: - return handlers.WrapError(err, "failed to verify subscription notification", - http.StatusInternalServerError) - } - } - - return handlers.RenderContent(ctx, "event received", w, http.StatusOK) - } -} - -// HandleIOSWebhook is the handler for ios iap webhooks -func HandleIOSWebhook(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - - var ( - ctx = r.Context() - req = new(IOSNotification) - validationErrMap = map[string]interface{}{} // for tracking our validation errors - ) - - // get logger - logger := logging.Logger(ctx, "payments").With(). - Str("func", "HandleIOSWebhook"). - Logger() - - // read the payload - payload, err := requestutils.Read(r.Context(), r.Body) - if err != nil { - logger.Error().Err(err).Msg("failed to read the payload") - // no need to go further - return handlers.WrapValidationError(err) - } - - // validate the payload - if err := inputs.DecodeAndValidate(context.Background(), req, payload); err != nil { - logger.Debug().Str("payload", string(payload)).Msg("failed to decode and validate the payload") - logger.Warn().Err(err).Msg("failed to decode and validate the payload") - validationErrMap["request-body-decode"] = err.Error() - } - - // transaction info - txInfo, err := req.GetTransactionInfo(ctx) - if err != nil { - logger.Warn().Err(err).Msg("failed to get transaction info from message") - validationErrMap["invalid-transaction-info"] = err.Error() - } - - // renewal info - renewalInfo, err := req.GetRenewalInfo(ctx) - if err != nil { - logger.Warn().Err(err).Msg("failed to get renewal info from message") - validationErrMap["invalid-renewal-info"] = err.Error() - } - - // if we had any validation errors, return the validation error map to the caller - if len(validationErrMap) != 0 { - return handlers.ValidationError("Error validating request url", validationErrMap) - } - - err = service.verifyIOSNotification(ctx, txInfo, renewalInfo) - if err != nil { - logger.Error().Err(err).Msg("failed to verify ios subscription notification") - switch { - case errors.Is(err, errNotFound): - return handlers.WrapError(err, "failed to verify ios subscription notification", - http.StatusNotFound) - default: - return handlers.WrapError(err, "failed to verify ios subscription notification", - http.StatusInternalServerError) - } - } - return handlers.RenderContent(ctx, "event received", w, http.StatusOK) - } -} - -// HandleRadomWebhook handles Radom checkout session webhooks. -func HandleRadomWebhook(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - - lg := logging.Logger(ctx, "payments").With().Str("func", "HandleRadomWebhook").Logger() - - // Get webhook secret. - endpointSecret, err := appctx.GetStringFromContext(ctx, appctx.RadomWebhookSecretCTXKey) - if err != nil { - lg.Error().Err(err).Msg("failed to get radom_webhook_secret from context") - return handlers.WrapError(err, "error getting radom_webhook_secret from context", http.StatusInternalServerError) - } - - // Check verification key. - if subtle.ConstantTimeCompare([]byte(r.Header.Get("radom-verification-key")), []byte(endpointSecret)) != 1 { - lg.Error().Err(err).Msg("invalid verification key from webhook") - return handlers.WrapError(err, "invalid verification key", http.StatusBadRequest) - } - - req := radom.WebhookRequest{} - if err := requestutils.ReadJSON(ctx, r.Body, &req); err != nil { - lg.Error().Err(err).Msg("failed to read request body") - return handlers.WrapError(err, "error reading request body", http.StatusServiceUnavailable) - } - - lg.Debug().Str("event_type", req.EventType).Str("data", fmt.Sprintf("%+v", req)).Msg("webhook event captured") - - // Handle only successful payment events. - if req.EventType != "managedRecurringPayment" && req.EventType != "newSubscription" { - return handlers.WrapError(err, "event type not implemented", http.StatusBadRequest) - } - - // Lookup the order, the checkout session was created with orderId in metadata. - rawOrderID, err := req.Data.CheckoutSession.Metadata.Get("braveOrderId") - if err != nil || rawOrderID == "" { - return handlers.WrapError(err, "brave metadata not found in webhook", http.StatusBadRequest) - } - - orderID, err := uuid.FromString(rawOrderID) - if err != nil { - return handlers.WrapError(err, "invalid braveOrderId in request", http.StatusBadRequest) - } - - // Set order id to paid, and update metadata values. - if err := service.Datastore.UpdateOrder(orderID, OrderStatusPaid); err != nil { - lg.Error().Err(err).Msg("failed to update order status") - return handlers.WrapError(err, "error updating order status", http.StatusInternalServerError) - } - - if err := service.Datastore.AppendOrderMetadata( - ctx, &orderID, "radomCheckoutSession", req.Data.CheckoutSession.CheckoutSessionID); err != nil { - lg.Error().Err(err).Msg("failed to update order metadata") - return handlers.WrapError(err, "error updating order metadata", http.StatusInternalServerError) - } - - if req.EventType == "newSubscription" { - - if err := service.Datastore.AppendOrderMetadata( - ctx, &orderID, "subscriptionId", req.EventData.NewSubscription.SubscriptionID); err != nil { - lg.Error().Err(err).Msg("failed to update order metadata") - return handlers.WrapError(err, "error updating order metadata", http.StatusInternalServerError) - } - - if err := service.Datastore.AppendOrderMetadata( - ctx, &orderID, "subscriptionContractAddress", - req.EventData.NewSubscription.Subscription.AutomatedEVMSubscription.SubscriptionContractAddress); err != nil { - - lg.Error().Err(err).Msg("failed to update order metadata") - return handlers.WrapError(err, "error updating order metadata", http.StatusInternalServerError) - } - - } - - // Set paymentProcessor to Radom. - if err := service.Datastore.AppendOrderMetadata(ctx, &orderID, paymentProcessor, model.RadomPaymentMethod); err != nil { - lg.Error().Err(err).Msg("failed to update order to add the payment processor") - return handlers.WrapError(err, "failed to update order to add the payment processor", http.StatusInternalServerError) - } - - lg.Debug().Str("orderID", orderID.String()).Msg("order is now paid") - return handlers.RenderContent(ctx, "payment successful", w, http.StatusOK) - } -} - -// HandleStripeWebhook handles webhook events from Stripe. -func HandleStripeWebhook(service *Service) handlers.AppHandler { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - - lg := logging.Logger(ctx, "payments").With().Str("func", "HandleStripeWebhook").Logger() - - endpointSecret, err := appctx.GetStringFromContext(ctx, appctx.StripeWebhookSecretCTXKey) - if err != nil { - lg.Error().Err(err).Msg("failed to get stripe_webhook_secret from context") - return handlers.WrapError(err, "error getting stripe_webhook_secret from context", http.StatusInternalServerError) - } - - b, err := requestutils.Read(ctx, r.Body) - if err != nil { - lg.Error().Err(err).Msg("failed to read request body") - return handlers.WrapError(err, "error reading request body", http.StatusServiceUnavailable) - } - - event, err := webhook.ConstructEvent(b, r.Header.Get("Stripe-Signature"), endpointSecret) - if err != nil { - lg.Error().Err(err).Msg("failed to verify stripe signature") - return handlers.WrapError(err, "error verifying webhook signature", http.StatusBadRequest) - } - - switch event.Type { - case StripeInvoiceUpdated, StripeInvoicePaid: - // Handle invoice events. - - var invoice stripe.Invoice - if err := json.Unmarshal(event.Data.Raw, &invoice); err != nil { - lg.Error().Err(err).Msg("error parsing webhook json") - return handlers.WrapError(err, "error parsing webhook JSON", http.StatusBadRequest) - } - - subscription, err := service.scClient.Subscriptions.Get(invoice.Subscription.ID, nil) - if err != nil { - lg.Error().Err(err).Msg("error getting subscription") - return handlers.WrapError(err, "error retrieving subscription", http.StatusInternalServerError) - } - - orderID, err := uuid.FromString(subscription.Metadata["orderID"]) - if err != nil { - lg.Error().Err(err).Msg("error getting order id from subscription metadata") - return handlers.WrapError(err, "error retrieving orderID", http.StatusInternalServerError) - } - - // If the invoice is paid set order status to paid, otherwise - if invoice.Paid { - ok, subID, err := service.Datastore.IsStripeSub(orderID) - if err != nil { - lg.Error().Err(err).Msg("failed to tell if this is a stripe subscription") - return handlers.WrapError(err, "error looking up payment provider", http.StatusInternalServerError) - } - - // Handle renewal. - if ok && subID != "" { - if err := service.RenewOrder(ctx, orderID); err != nil { - lg.Error().Err(err).Msg("failed to renew the order") - return handlers.WrapError(err, "error renewing order", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, "subscription renewed", w, http.StatusOK) - } - - // New subscription. - // Update the order's expires at as it was just paid. - if err := service.Datastore.UpdateOrder(orderID, OrderStatusPaid); err != nil { - lg.Error().Err(err).Msg("failed to update order status") - return handlers.WrapError(err, "error updating order status", http.StatusInternalServerError) - } - - if err := service.Datastore.AppendOrderMetadata(ctx, &orderID, "stripeSubscriptionId", subscription.ID); err != nil { - lg.Error().Err(err).Msg("failed to update order metadata") - return handlers.WrapError(err, "error updating order metadata", http.StatusInternalServerError) - } - - if err := service.Datastore.AppendOrderMetadata(ctx, &orderID, paymentProcessor, StripePaymentMethod); err != nil { - lg.Error().Err(err).Msg("failed to update order to add the payment processor") - return handlers.WrapError(err, "failed to update order to add the payment processor", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, "payment successful", w, http.StatusOK) - } - - if err := service.Datastore.UpdateOrder(orderID, "pending"); err != nil { - lg.Error().Err(err).Msg("failed to update order status") - return handlers.WrapError(err, "error updating order status", http.StatusInternalServerError) - } - - if err := service.Datastore.AppendOrderMetadata(ctx, &orderID, "stripeSubscriptionId", subscription.ID); err != nil { - lg.Error().Err(err).Msg("failed to update order metadata") - return handlers.WrapError(err, "error updating order metadata", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, "payment failed", w, http.StatusOK) - - case StripeCustomerSubscriptionDeleted: - // Handle subscription cancellations - - var subscription stripe.Subscription - if err := json.Unmarshal(event.Data.Raw, &subscription); err != nil { - return handlers.WrapError(err, "error parsing webhook JSON", http.StatusBadRequest) - } - - orderID, err := uuid.FromString(subscription.Metadata["orderID"]) - if err != nil { - return handlers.WrapError(err, "error retrieving orderID", http.StatusInternalServerError) - } - - if err := service.Datastore.UpdateOrder(orderID, OrderStatusCanceled); err != nil { - return handlers.WrapError(err, "error updating order status", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, "subscription canceled", w, http.StatusOK) - } - - return handlers.RenderContent(ctx, "event received", w, http.StatusOK) - } -} - -// SubmitReceipt submit a vendor verifiable receipt that proves order is paid -func SubmitReceipt(service *Service) handlers.AppHandler { - return handlers.AppHandler(func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - - var ( - ctx = r.Context() - req SubmitReceiptRequestV1 // the body of the request - orderID = new(inputs.ID) // the order id - validationErrMap = map[string]interface{}{} // for tracking our validation errors - ) - - logger := logging.Logger(ctx, "skus").With().Str("func", "SubmitReceipt").Logger() - - // validate the order id - if err := inputs.DecodeAndValidateString(context.Background(), orderID, chi.URLParam(r, "orderID")); err != nil { - logger.Warn().Err(err).Msg("Failed to decode/validate order id from url") - validationErrMap["orderID"] = err.Error() - } - - // read the payload - payload, err := requestutils.Read(r.Context(), r.Body) - if err != nil { - logger.Warn().Err(err).Msg("Failed to read the payload") - validationErrMap["request-body"] = err.Error() - } - - // validate the payload - if err := inputs.DecodeAndValidate(context.Background(), &req, payload); err != nil { - logger.Debug().Str("payload", string(payload)).Msg("Failed to decode and validate the payload") - logger.Warn().Err(err).Msg("Failed to decode and validate the payload") - validationErrMap["request-body"] = err.Error() - } - - // validate the receipt - externalID, err := service.validateReceipt(ctx, orderID.UUID(), req) - if err != nil { - if errors.Is(err, errNotFound) { - return handlers.WrapError(err, "order not found", http.StatusNotFound) - } - logger.Warn().Err(err).Msg("Failed to validate the receipt with vendor") - validationErrMap["receiptErrors"] = err.Error() - // return codified errors for application - if errors.Is(err, errPurchaseFailed) { - return handlers.CodedValidationError(err.Error(), purchaseFailedErrCode, validationErrMap) - } else if errors.Is(err, errPurchasePending) { - return handlers.CodedValidationError(err.Error(), purchasePendingErrCode, validationErrMap) - } else if errors.Is(err, errPurchaseDeferred) { - return handlers.CodedValidationError(err.Error(), purchaseDeferredErrCode, validationErrMap) - } else if errors.Is(err, errPurchaseStatusUnknown) { - return handlers.CodedValidationError(err.Error(), purchaseStatusUnknownErrCode, validationErrMap) - } else { - // unknown error - return handlers.CodedValidationError("error validating receipt", purchaseValidationErrCode, validationErrMap) - } - } - - // if we had any validation errors, return the validation error map to the caller - if len(validationErrMap) != 0 { - return handlers.ValidationError("error validating request", validationErrMap) - } - // does this external id exist already - exists, err := service.ExternalIDExists(ctx, externalID) - if err != nil { - logger.Warn().Err(err).Msg("failed to lookup external id existance") - return handlers.WrapError(err, "failed to lookup external id", http.StatusInternalServerError) - } - - if exists { - return handlers.WrapError(err, "receipt has already been submitted", http.StatusBadRequest) - } - - // set order paid and include the vendor and external id to metadata - if err := service.UpdateOrderStatusPaidWithMetadata(ctx, orderID.UUID(), datastore.Metadata{ - "vendor": req.Type.String(), - "externalID": externalID, - paymentProcessor: req.Type.String(), - }); err != nil { - logger.Warn().Err(err).Msg("Failed to update the order with appropriate metadata") - return handlers.WrapError(err, "failed to store status of order", http.StatusInternalServerError) - } - - return handlers.RenderContent(r.Context(), SubmitReceiptResponseV1{ - ExternalID: externalID, - Vendor: req.Type.String(), - }, w, http.StatusOK) - }) -} - -func NewCORSMwr(opts cors.Options, methods ...string) func(next http.Handler) http.Handler { - opts.AllowedMethods = methods - - return cors.Handler(opts) -} - -func NewCORSOpts(origins []string, dbg bool) cors.Options { - result := cors.Options{ - Debug: dbg, - AllowedOrigins: origins, - AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"}, - ExposedHeaders: []string{""}, - AllowCredentials: false, - MaxAge: 300, // Maximum value not ignored by any of major browsers - } - - return result -} diff --git a/services/skus/controllers_test.go b/services/skus/controllers_test.go deleted file mode 100644 index 1cb5fa7a3..000000000 --- a/services/skus/controllers_test.go +++ /dev/null @@ -1,1892 +0,0 @@ -//go:build integration - -package skus - -import ( - "bytes" - "context" - "database/sql" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "os" - "strconv" - "strings" - "testing" - "time" - - "github.com/asaskevich/govalidator" - "github.com/go-chi/chi" - "github.com/go-chi/cors" - "github.com/golang/mock/gomock" - "github.com/linkedin/goavro" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/libs/backoff/retrypolicy" - "github.com/brave-intl/bat-go/libs/clients/cbr" - mockcb "github.com/brave-intl/bat-go/libs/clients/cbr/mock" - "github.com/brave-intl/bat-go/libs/clients/gemini" - mockgemini "github.com/brave-intl/bat-go/libs/clients/gemini/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/brave-intl/bat-go/libs/test" - timeutils "github.com/brave-intl/bat-go/libs/time" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/brave-intl/bat-go/services/skus/handler" - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/skustest" - "github.com/brave-intl/bat-go/services/wallet" - macaroon "github.com/brave-intl/bat-go/tools/macaroon/cmd" - - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -var ( - // these skus will be generated with the appropriate merchant key in setup - UserWalletVoteToken macaroon.Token - UserWalletVoteTestSkuToken string - UserWalletVoteSmallToken macaroon.Token - UserWalletVoteTestSmallSkuToken string - - AnonCardToken macaroon.Token - AnonCardVoteTestSkuToken string - - FreeTestToken macaroon.Token - FreeTestSkuToken string - - FreeTLTestToken macaroon.Token - FreeTLTestSkuToken string - - FreeTLTest1MToken macaroon.Token - FreeTLTest1MSkuToken string - - InvalidFreeTestSkuToken string -) - -type ControllersTestSuite struct { - service *Service - mockCB *mockcb.MockClient - mockCtrl *gomock.Controller - storage Datastore - suite.Suite - orderh *handler.Order -} - -func TestControllersTestSuite(t *testing.T) { - suite.Run(t, new(ControllersTestSuite)) -} - -func (suite *ControllersTestSuite) SetupSuite() { - skustest.Migrate(suite.T()) - retryPolicy = retrypolicy.NoRetry // set this so we fail fast for cbr http requests - govalidator.SetFieldsRequiredByDefault(true) - - storage, _ := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - - suite.storage = storage - - AnonCardC := macaroon.Caveats{ - "sku": "anon-card-vote", - "description": "brave anon-card-vote sku token v1", - "credential_type": "single-use", - "currency": "BAT", - "price": "0.25", - } - - UserWalletC := macaroon.Caveats{ - "sku": "user-wallet-vote", - "description": "brave user-wallet-vote sku token v1", - "credential_type": "single-use", - "currency": "BAT", - "price": "0.25", - } - - UserWalletSmallC := macaroon.Caveats{ - "sku": "user-wallet-vote", - "description": "brave user-wallet-vote sku token v1", - "credential_type": "single-use", - "currency": "BAT", - "price": "0.00000000000000001", - } - - FreeC := macaroon.Caveats{ - "sku": "integration-test-free", - "description": "integration test free sku token", - "credential_type": "single-use", - "currency": "BAT", - "price": "0.00", - } - - FreeTLC := macaroon.Caveats{ - "sku": "integration-test-free", - "description": "integration test free sku token", - "credential_type": "time-limited", - "credential_valid_duration": "P1M", - "currency": "BAT", - "price": "0.00", - } - - FreeTL1MC := macaroon.Caveats{ - "sku": "integration-test-free-1m", - "description": "integration test free sku token", - "credential_type": "time-limited", - "credential_valid_duration": "P1M", - "issuance_interval": "P1M", - "currency": "BAT", - "price": "0.00", - } - - // create sku using key - UserWalletVoteToken = macaroon.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{UserWalletC}, - } - - UserWalletVoteSmallToken = macaroon.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{UserWalletSmallC}, - } - - AnonCardToken = macaroon.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{AnonCardC}, - } - - FreeTestToken = macaroon.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{FreeC}, - } - - FreeTLTestToken = macaroon.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{FreeTLC}, - } - - FreeTLTest1MToken = macaroon.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{FreeTL1MC}, - } - - var err error - // setup our global skus - UserWalletVoteTestSkuToken, err = UserWalletVoteToken.Generate("testing123") - suite.Require().NoError(err) - - // hacky, put this in development sku check - skuMap["development"][UserWalletVoteTestSkuToken] = true - - UserWalletVoteTestSmallSkuToken, err = UserWalletVoteSmallToken.Generate("testing123") - suite.Require().NoError(err) - - // hacky, put this in development sku check - skuMap["development"][UserWalletVoteTestSmallSkuToken] = true - - AnonCardVoteTestSkuToken, err = AnonCardToken.Generate("testing123") - suite.Require().NoError(err) - - // hacky, put this in development sku check - skuMap["development"][AnonCardVoteTestSkuToken] = true - - FreeTestSkuToken, err = FreeTestToken.Generate("testing123") - suite.Require().NoError(err) - - // hacky, put this in development sku check - skuMap["development"][FreeTestSkuToken] = true - - FreeTLTestSkuToken, err = FreeTLTestToken.Generate("testing123") - suite.Require().NoError(err) - - FreeTLTest1MSkuToken, err = FreeTLTest1MToken.Generate("testing123") - suite.Require().NoError(err) - - // hacky, put this in development sku check - skuMap["development"][FreeTLTestSkuToken] = true - skuMap["development"][FreeTLTest1MSkuToken] = true - - // signed with wrong signing string - InvalidFreeTestSkuToken, err = FreeTestToken.Generate("123testing") - suite.Require().NoError(err) -} - -func (suite *ControllersTestSuite) BeforeTest(sn, tn string) { - pg, err := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.Require().NoError(err, "Failed to get postgres conn") - - suite.mockCtrl = gomock.NewController(suite.T()) - suite.mockCB = mockcb.NewMockClient(suite.mockCtrl) - - walletDB, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - EncryptionKey = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0" - InitEncryptionKeys() - - suite.service = &Service{ - issuerRepo: repository.NewIssuer(), - Datastore: pg, - cbClient: suite.mockCB, - wallet: &wallet.Service{ - Datastore: walletDB, - }, - retry: backoff.Retry, - } - - suite.orderh = handler.NewOrder(suite.service) - - // encrypt merchant key - cipher, nonce, err := cryptography.EncryptMessage(byteEncryptionKey, []byte("testing123")) - suite.Require().NoError(err) - - // create key in db for our brave.com location - _, err = suite.service.Datastore.CreateKey("brave.com", "brave.com", hex.EncodeToString(cipher), hex.EncodeToString(nonce[:])) - suite.Require().NoError(err) -} - -func (suite *ControllersTestSuite) AfterTest(sn, tn string) { - skustest.CleanDB(suite.T(), suite.storage.RawDB()) - suite.mockCtrl.Finish() -} - -func (suite *ControllersTestSuite) setupCreateOrder(skuToken string, token macaroon.Token, quantity int) (Order, *Issuer) { - issuerID, err := encodeIssuerID(token.Location, token.FirstPartyCaveats[0]["sku"]) - suite.Require().NoError(err) - - // Mock out create issuer calls before we create the order. - credType, ok := token.FirstPartyCaveats[0]["credential_type"] - if ok && credType == singleUse { - suite.mockCB.EXPECT().CreateIssuer(gomock.Any(), issuerID, gomock.Any()).Return(nil) - - resp := &cbr.IssuerResponse{ - Name: issuerID, - PublicKey: base64.StdEncoding.EncodeToString([]byte(test.RandomString())), - } - - suite.mockCB.EXPECT().GetIssuer(gomock.Any(), gomock.Any()).Return(resp, nil) - } - - // create order this will also create the issuer - - createRequest := &model.CreateOrderRequest{ - Items: []model.OrderItemRequest{ - { - SKU: skuToken, - Quantity: quantity, - }, - }, - } - - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/orders", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - req = req.WithContext(context.WithValue(req.Context(), appctx.EnvironmentCTXKey, "development")) - - rr := httptest.NewRecorder() - - handlers.AppHandler(suite.orderh.Create).ServeHTTP(rr, req) - - suite.Require().Equal(http.StatusCreated, rr.Code) - - var order Order - { - err := json.Unmarshal(rr.Body.Bytes(), &order) - suite.Require().NoError(err) - } - - repo := repository.NewIssuer() - - var exp error - if credType == timeLimited { - exp = model.ErrIssuerNotFound - } - - issuer, err := repo.GetByMerchID(context.TODO(), suite.storage.RawDB(), issuerID) - suite.Require().Equal(exp, err) - - return order, issuer -} - -func (suite *ControllersTestSuite) TestIOSWebhookCertFail() { - order, _ := suite.setupCreateOrder(UserWalletVoteTestSkuToken, UserWalletVoteToken, 40) - suite.Assert().NotNil(order) - - // Check the order - suite.Assert().Equal("10", order.TotalPrice.String()) - - // add the external id to metadata as if an initial receipt was submitted - err := suite.service.Datastore.AppendOrderMetadata(context.Background(), &order.ID, "externalID", "my external id") - suite.Require().NoError(err) - - handler := HandleIOSWebhook(suite.service) - - // create a jws message to send - body := []byte{} - - // create request to webhook - req, err := http.NewRequest("POST", "/v1/ios", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - req = req.WithContext(context.WithValue(req.Context(), appctx.EnvironmentCTXKey, "development")) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - suite.Require().Equal(http.StatusBadRequest, rr.Code) -} - -func (suite *ControllersTestSuite) TestAndroidWebhook() { - order, _ := suite.setupCreateOrder(UserWalletVoteTestSkuToken, UserWalletVoteToken, 40) - suite.Assert().NotNil(order) - - // Check the order - suite.Assert().Equal("10", order.TotalPrice.String()) - - // add the external id to metadata as if an initial receipt was submitted - err := suite.storage.AppendOrderMetadata(context.Background(), &order.ID, "externalID", "my external id") - suite.Require().NoError(err) - - // overwrite the receipt validation function for this test - receiptValidationFns = map[Vendor]func(context.Context, interface{}) (string, error){ - appleVendor: validateIOSReceipt, - googleVendor: func(ctx context.Context, v interface{}) (string, error) { - return "my external id", nil - }, - } - - handler := HandleAndroidWebhook(suite.service) - - // notification message - devNotify := DeveloperNotification{ - PackageName: "package name", - SubscriptionNotification: SubscriptionNotification{ - NotificationType: androidSubscriptionCanceled, - PurchaseToken: "my external id", - SubscriptionID: "subscription id", - }, - } - - buf, err := json.Marshal(&devNotify) - suite.Require().NoError(err) - - // wrapper notification message - notification := &AndroidNotification{ - Message: AndroidNotificationMessage{ - Data: base64.StdEncoding.EncodeToString(buf), // dev notification is b64 encoded - }, - Subscription: "subscription", - } - - body, err := json.Marshal(¬ification) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/android", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - req = req.WithContext(context.WithValue(req.Context(), appctx.EnvironmentCTXKey, "development")) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - suite.Require().Equal(http.StatusOK, rr.Code) - - // get order and check the state changed to canceled - updatedOrder, err := suite.service.Datastore.GetOrder(order.ID) - suite.Assert().Equal("canceled", updatedOrder.Status) -} - -func (suite *ControllersTestSuite) TestCreateOrder() { - order, _ := suite.setupCreateOrder(UserWalletVoteTestSkuToken, UserWalletVoteToken, 40) - - // Check the order - suite.Assert().Equal("10", order.TotalPrice.String()) - suite.Assert().Equal("pending", order.Status) - suite.Assert().Equal("BAT", order.Currency) - - // Check the order items - suite.Assert().Equal(len(order.Items), 1) - suite.Assert().Equal("BAT", order.Items[0].Currency) - suite.Assert().Equal("0.25", order.Items[0].Price.String()) - suite.Assert().Equal(40, order.Items[0].Quantity) - suite.Assert().Equal(decimal.New(10, 0), order.Items[0].Subtotal) - suite.Assert().Equal(order.ID, order.Items[0].OrderID) - suite.Assert().Equal("user-wallet-vote", order.Items[0].SKU) -} - -func (suite *ControllersTestSuite) TestCreateFreeOrderWhitelistedSKU() { - order, _ := suite.setupCreateOrder(FreeTestSkuToken, FreeTestToken, 10) - - // Check the order - suite.Assert().Equal("0", order.TotalPrice.String()) - suite.Assert().Equal("paid", order.Status) - suite.Assert().Equal("BAT", order.Currency) - - // Check the order items - suite.Assert().Equal(len(order.Items), 1) - suite.Assert().Equal("BAT", order.Items[0].Currency) - suite.Assert().Equal("0", order.Items[0].Price.String()) - suite.Assert().Equal(10, order.Items[0].Quantity) - suite.Assert().Equal(decimal.New(0, 0), order.Items[0].Subtotal) - suite.Assert().Equal(order.ID, order.Items[0].OrderID) - suite.Assert().Equal("integration-test-free", order.Items[0].SKU) -} - -func (suite *ControllersTestSuite) TestCreateInvalidOrder() { - createRequest := &model.CreateOrderRequest{ - Items: []model.OrderItemRequest{ - { - SKU: InvalidFreeTestSkuToken, - Quantity: 1, - }, - }, - } - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/orders", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - req = req.WithContext(context.WithValue(req.Context(), appctx.EnvironmentCTXKey, "development")) - - rr := httptest.NewRecorder() - - handlers.AppHandler(suite.orderh.Create).ServeHTTP(rr, req) - suite.Require().Equal(http.StatusBadRequest, rr.Code) - - suite.Require().Contains(rr.Body.String(), "Invalid SKU Token provided in request") -} - -func (suite *ControllersTestSuite) TestGetOrder() { - order, _ := suite.setupCreateOrder(UserWalletVoteTestSkuToken, UserWalletVoteToken, 20) - - req, err := http.NewRequest("GET", "/v1/orders/{orderID}", nil) - suite.Require().NoError(err) - - getOrderHandler := GetOrder(suite.service) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - getReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - getOrderHandler.ServeHTTP(rr, getReq) - suite.Require().Equal(http.StatusOK, rr.Code) - - err = json.Unmarshal(rr.Body.Bytes(), &order) - suite.Require().NoError(err) - - suite.Assert().Equal("5", order.TotalPrice.String()) - suite.Assert().Equal("pending", order.Status) - - // Check the order items - suite.Assert().Equal(len(order.Items), 1) - suite.Assert().Equal("BAT", order.Items[0].Currency) - suite.Assert().Equal("0.25", order.Items[0].Price.String()) - suite.Assert().Equal(20, order.Items[0].Quantity) - suite.Assert().Equal(decimal.New(5, 0), order.Items[0].Subtotal) - suite.Assert().Equal(order.ID, order.Items[0].OrderID) -} - -func (suite *ControllersTestSuite) TestGetMissingOrder() { - req, err := http.NewRequest("GET", "/v1/orders/{orderID}", nil) - suite.Require().NoError(err) - - getOrderHandler := GetOrder(suite.service) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("orderID", "9645ca16-bc93-4e37-8edf-cb35b1763216") - getReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - getOrderHandler.ServeHTTP(rr, getReq) - suite.Assert().Equal(http.StatusNotFound, rr.Code) -} - -func (suite *ControllersTestSuite) TestE2EOrdersGeminiTransactions() { - pg, err := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.Require().NoError(err, "Failed to get postgres conn") - - service := &Service{ - Datastore: pg, - } - order, _ := suite.setupCreateOrder(UserWalletVoteTestSkuToken, UserWalletVoteToken, 1/.25) - - handler := CreateGeminiTransaction(service) - - createRequest := &CreateTransactionRequest{ - ExternalTransactionID: "150d7a21-c203-4ba4-8fdf-c5fc36aca004", - } - - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/orders/{orderID}/transactions/gemini", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - postReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - // setup fake gemini client - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - mockGemini := mockgemini.NewMockClient(mockCtrl) - - settlementAddress := "settlement" - currency := "BAT" - status := "completed" - amount, err := decimal.NewFromString("0.0000000001") - suite.Require().NoError(err) - // make sure we get a call to CheckTxStatus and return the right things - mockGemini.EXPECT(). - CheckTxStatus(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return( - &gemini.PayoutResult{ - Destination: &settlementAddress, - Amount: &amount, - Currency: ¤cy, - Status: &status, - }, nil) - - // setup context and client - service.geminiClient = mockGemini - service.geminiConf = &gemini.Conf{ - APIKey: "key", - Secret: "secret", - ClientID: "client_id", - SettlementAddress: settlementAddress, - } - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, postReq) - - suite.Require().Equal(http.StatusCreated, rr.Code) - - var transaction Transaction - err = json.Unmarshal(rr.Body.Bytes(), &transaction) - suite.Require().NoError(err) - - // Check the transaction - suite.Assert().Equal(amount, transaction.Amount) - suite.Assert().Equal("gemini", transaction.Kind) - suite.Assert().Equal("completed", transaction.Status) - suite.Assert().Equal("BAT", transaction.Currency) - suite.Assert().Equal(createRequest.ExternalTransactionID, transaction.ExternalTransactionID) - suite.Assert().Equal(order.ID, transaction.OrderID, order.TotalPrice) - - // Check the order was updated to paid - // Old order - suite.Assert().Equal("pending", order.Status) - // Check the new order - - // this is not possible to test end to end, settlement bots are out of our control - // and sometimes take upwards of 10 minutes. Only reason this worked before was - // we had asked them to make them run quicker... Not sure this is a good test - // FIXME: figure out how we can do this without waiting for their settlement bots - //updatedOrder, err := service.Datastore.GetOrder(order.ID) - //suite.Require().NoError(err) - //suite.Assert().Equal("paid", updatedOrder.Status) - - // make sure we get a call to CheckTxStatus and return the right things - mockGemini.EXPECT(). - CheckTxStatus(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return( - &gemini.PayoutResult{ - Destination: &settlementAddress, - Amount: &amount, - Currency: ¤cy, - Status: &status, - }, nil) - - req, err = http.NewRequest("POST", "/v1/orders/{orderID}/transactions/gemini", bytes.NewBuffer(body)) - - rctx = chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - postReq = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - // setup context and client - service.geminiClient = mockGemini - service.geminiConf = &gemini.Conf{ - APIKey: "key", - Secret: "secret", - ClientID: "client_id", - SettlementAddress: settlementAddress, - } - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, postReq) - // now should be a 200 for updating tx - suite.Require().Equal(http.StatusOK, rr.Code) -} - -func (suite *ControllersTestSuite) TestE2EOrdersUpholdTransactions() { - orderAmount := decimal.NewFromFloat(0.00000000000000001) - - order, _ := suite.setupCreateOrder(UserWalletVoteTestSmallSkuToken, UserWalletVoteToken, 1) - - handler := CreateUpholdTransaction(suite.service) - - // create an uphold wallet - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err, "Failed to create wallet keypair") - - walletID := uuid.NewV4() - bat := altcurrency.BAT - info := walletutils.Info{ - ID: walletID.String(), - Provider: "uphold", - ProviderID: "-", - AltCurrency: &bat, - PublicKey: hex.EncodeToString(publicKey), - LastBalance: nil, - } - - ctx := context.Background() - // setup debug for client - ctx = context.WithValue(ctx, appctx.DebugLoggingCTXKey, true) - // setup debug log level - ctx = context.WithValue(ctx, appctx.LogLevelCTXKey, "debug") - - // setup a new logger, add to context as well - _, logger := logutils.SetupLogger(ctx) - - w := uphold.Wallet{ - Logger: logger, - Info: info, - PrivKey: privKey, - PubKey: publicKey, - } - err = w.Register(ctx, "drain-card-test") - suite.Require().NoError(err, "Failed to register wallet") - - _, err = uphold.FundWallet(ctx, &w, altcurrency.BAT.ToProbi(orderAmount)) - suite.Require().NoError(err, "Failed to fund wallet") - - <-time.After(1 * time.Second) - - // pay the transaction - settlementAddr := os.Getenv("BAT_SETTLEMENT_ADDRESS") - tInfo, err := w.Transfer(ctx, altcurrency.BAT, altcurrency.BAT.ToProbi(orderAmount), settlementAddr) - suite.Require().NoError(err) - - createRequest := &CreateTransactionRequest{ - ExternalTransactionID: tInfo.ID, - } - - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - - req, err := http.NewRequest("POST", "/v1/orders/{orderID}/transactions/uphold", bytes.NewBuffer(body)) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - postReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - suite.Require().NoError(err) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, postReq) - - suite.Require().Equal(http.StatusCreated, rr.Code) - - var transaction Transaction - err = json.Unmarshal(rr.Body.Bytes(), &transaction) - suite.Require().NoError(err) - - // Check the transaction - suite.Assert().Equal(orderAmount, transaction.Amount) - suite.Assert().Equal("uphold", transaction.Kind) - suite.Assert().Equal("completed", transaction.Status) - suite.Assert().Equal("BAT", transaction.Currency) - suite.Assert().Equal(createRequest.ExternalTransactionID, transaction.ExternalTransactionID) - suite.Assert().Equal(order.ID, transaction.OrderID, order.TotalPrice) - - // Check the order was updated to paid - // Old order - suite.Assert().Equal("pending", order.Status) - // Check the new order - updatedOrder, err := suite.service.Datastore.GetOrder(order.ID) - suite.Require().NoError(err) - suite.Assert().Equal("paid", updatedOrder.Status) - - // Test to make sure on repost we update tx - req, err = http.NewRequest("POST", "/v1/orders/{orderID}/transactions/uphold", bytes.NewBuffer(body)) - - rctx = chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - postReq = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - suite.Require().NoError(err) - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, postReq) - - // now it should be a 200 when updating a tx status - suite.Require().Equal(http.StatusOK, rr.Code) -} - -func (suite *ControllersTestSuite) TestGetTransactions() { - // External transaction has 12 BAT - order, _ := suite.setupCreateOrder(UserWalletVoteTestSkuToken, UserWalletVoteToken, 12/.25) - - handler := CreateUpholdTransaction(suite.service) - - createRequest := &CreateTransactionRequest{ - ExternalTransactionID: "9d5b6a7d-795b-4f02-a91e-25eee2852ebf", - } - - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - - oldUpholdSettlementAddress := uphold.UpholdSettlementAddress - uphold.UpholdSettlementAddress = "6654ecb0-6079-4f6c-ba58-791cc890a561" - - defer func() { - uphold.UpholdSettlementAddress = oldUpholdSettlementAddress - }() - - req, err := http.NewRequest("POST", "/v1/orders/{orderID}/transactions/uphold", bytes.NewBuffer(body)) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - postReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - suite.Require().NoError(err) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, postReq) - - suite.Require().Equal(http.StatusCreated, rr.Code) - - var transaction Transaction - err = json.Unmarshal(rr.Body.Bytes(), &transaction) - suite.Require().NoError(err) - - // Check the transaction - suite.Assert().Equal(decimal.NewFromFloat32(12), transaction.Amount) - suite.Assert().Equal("uphold", transaction.Kind) - suite.Assert().Equal("completed", transaction.Status) - suite.Assert().Equal("BAT", transaction.Currency) - suite.Assert().Equal(createRequest.ExternalTransactionID, transaction.ExternalTransactionID) - suite.Assert().Equal(order.ID, transaction.OrderID) - - // Check the order was updated to paid - // Old order - suite.Assert().Equal("pending", order.Status) - // Check the new order - updatedOrder, err := suite.service.Datastore.GetOrder(order.ID) - suite.Require().NoError(err) - suite.Assert().Equal("paid", updatedOrder.Status) - - // Get all the transactions, should only be one - - handler = GetTransactions(suite.service) - req, err = http.NewRequest("GET", "/v1/orders/{orderID}/transactions", nil) - rctx = chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - getReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - suite.Require().NoError(err) - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, getReq) - - suite.Require().Equal(http.StatusOK, rr.Code) - var transactions []Transaction - err = json.Unmarshal(rr.Body.Bytes(), &transactions) - suite.Require().NoError(err) - - // Check the transaction - suite.Assert().Equal(decimal.NewFromFloat32(12), transactions[0].Amount) - suite.Assert().Equal("uphold", transactions[0].Kind) - suite.Assert().Equal("completed", transactions[0].Status) - suite.Assert().Equal("BAT", transactions[0].Currency) - suite.Assert().Equal(createRequest.ExternalTransactionID, transactions[0].ExternalTransactionID) - suite.Assert().Equal(order.ID, transactions[0].OrderID) -} - -func generateWallet(ctx context.Context, t *testing.T) *uphold.Wallet { - var info walletutils.Info - info.ID = uuid.NewV4().String() - info.Provider = "uphold" - info.ProviderID = "" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - if err != nil { - t.Fatal(err) - } - info.PublicKey = hex.EncodeToString(publicKey) - newWallet := &uphold.Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} - err = newWallet.Register(ctx, "bat-go test card") - if err != nil { - t.Fatal(err) - } - return newWallet -} - -func (suite *ControllersTestSuite) fetchTimeLimitedCredentials(service *Service, order Order) (ordercreds []TimeLimitedCreds) { - - o, err := suite.service.Datastore.GetOrder(order.ID) - var ii string - if o.Items == nil || o.Items[0].IssuanceIntervalISO == nil { - ii = "P1D" - } else { - ii = *(o.Items[0].IssuanceIntervalISO) - } - - // Check to see if we have HTTP Accepted - handler := GetOrderCreds(service) - req, err := http.NewRequest("GET", "/{orderID}/credentials", nil) - suite.Require().NoError(err) - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Assert().Equal(http.StatusOK, rr.Code) - - // see if we can get our order creds - handler = GetOrderCreds(service) - req, err = http.NewRequest("GET", "/{orderID}/credentials", nil) - suite.Require().NoError(err) - - rctx = chi.NewRouteContext() - rctx.URLParams.Add("orderID", order.ID.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - - err = json.Unmarshal([]byte(rr.Body.String()), &ordercreds) - suite.Require().NoError(err) - - isoD, err := timeutils.ParseDuration("P1M") // 1 month from the sku - suite.Require().NoError(err) - - ft, err := isoD.FromNow() - suite.Require().NoError(err) - - validFor := time.Until(*ft) - - suite.Require().NoError(err) - - // get the go duration of our issuance interval - interval, err := timeutils.ParseDuration(ii) - suite.Require().NoError(err) - chunk, err := interval.FromNow() - suite.Require().NoError(err) - - issuanceInterval := time.Until(*chunk) - - // validate we get the right number of creds back, 1 per day - numTokens := int(validFor / issuanceInterval) - suite.Require().True(numTokens <= len(ordercreds), "should have a buffer of tokens") - - return -} - -func (suite *ControllersTestSuite) fetchCredentials(ctx context.Context, server *http.Server, order Order, issuer Issuer) (signature, tokenPreimage string, ordercreds []OrderCreds) { - signature = "PsavkSWaqsTzZjmoDBmSu6YxQ7NZVrs2G8DQ+LkW5xOejRF6whTiuUJhr9dJ1KlA+79MDbFeex38X5KlnLzvJw==" - tokenPreimage = "125KIuuwtHGEl35cb5q1OLSVepoDTgxfsvwTc7chSYUM2Zr80COP19EuMpRQFju1YISHlnB04XJzZYN2ieT9Ng==" - - // perform create order creds request - credsReq := CreateOrderCredsRequest{ - ItemID: order.Items[0].ID, - BlindedCreds: []string{base64.StdEncoding.EncodeToString([]byte(test.RandomString()))}, - } - body, err := json.Marshal(credsReq) - suite.Require().NoError(err) - - req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/%s/credentials", order.ID), - bytes.NewBuffer(body)).WithContext(ctx) - rr := httptest.NewRecorder() - - server.Handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - - // check to see if order creds are waiting to be processed. - // We can expect a http accepted status when a job is submitted but not processed. - req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%s/credentials", order.ID), nil) - rr = httptest.NewRecorder() - - server.Handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusAccepted, rr.Code) - - // The CreateOrderCreds request writes to the db table signing_order_request_outbox which gets sent to Kafka - // and CBR for signing. To mock that insert the Kafka result. - - to := time.Now().Add(time.Hour).Format(time.RFC3339) - from := time.Now().Local().Format(time.RFC3339) - - metadata := Metadata{ - ItemID: order.Items[0].ID, - OrderID: order.ID, - IssuerID: issuer.ID, - CredentialType: order.Items[0].CredentialType, - } - - associatedData, err := json.Marshal(metadata) - suite.Require().NoError(err) - - signingOrderResult := SigningOrderResult{ - RequestID: uuid.NewV4().String(), - Data: []SignedOrder{ - { - PublicKey: issuer.PublicKey, - Proof: test.RandomString(), - Status: SignedOrderStatusOk, - BlindedTokens: credsReq.BlindedCreds, - SignedTokens: []string{test.RandomString()}, - ValidTo: &UnionNullString{"string": to}, - ValidFrom: &UnionNullString{"string": from}, - AssociatedData: associatedData, - }, - }, - } - - _, tx, _, commit, err := datastore.GetTx(ctx, suite.storage) - - err = suite.storage.InsertSignedOrderCredentialsTx(ctx, tx, &signingOrderResult) - suite.Require().NoError(err) - - err = commit() - suite.Require().NoError(err) - - // get the signed order credentials - req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%s/credentials", order.ID), nil) - - rr = httptest.NewRecorder() - - server.Handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - - err = json.NewDecoder(rr.Body).Decode(&ordercreds) - suite.Require().NoError(err) - - return -} - -func (suite *ControllersTestSuite) TestE2EAnonymousCard() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - voteTopic = test.RandomString() - kafkaSignedOrderCredsTopic = test.RandomString() - kafkaSignedOrderCredsDLQTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC_DLQ") - kafkaSignedRequestReaderGroupID = test.RandomString() - - ctx = skustest.SetupKafka(ctx, suite.T(), voteTopic, - kafkaUnsignedOrderCredsTopic, kafkaSignedOrderCredsTopic, kafkaSignedOrderCredsDLQTopic) - - err := suite.service.InitKafka(ctx) - suite.Require().NoError(err) - - authMwr := NewAuthMwr(suite.service) - instrumentHandler := func(name string, h http.Handler) http.Handler { - return h - } - - router := Router(suite.service, authMwr, instrumentHandler, newCORSOptsEnv()) - - router.Mount("/vote", VoteRouter(suite.service, instrumentHandler)) - server := &http.Server{Addr: ":8080", Handler: router} - - // NextVoteDrainJob monitors vote queue - go func() { - for { - select { - case <-ctx.Done(): - break - default: - _, _ = suite.service.RunNextVoteDrainJob(ctx) - <-time.After(50 * time.Millisecond) - } - } - }() - - numVotes := 1 - order, issuer := suite.setupCreateOrder(AnonCardVoteTestSkuToken, AnonCardToken, numVotes) - - userWallet := generateWallet(ctx, suite.T()) - err = suite.service.wallet.Datastore.UpsertWallet(ctx, &userWallet.Info) - suite.Require().NoError(err) - - balanceBefore, err := userWallet.GetBalance(ctx, true) - suite.Require().NoError(err) - balanceAfter, err := uphold.FundWallet(ctx, userWallet, order.TotalPrice) - suite.Require().NoError(err) - - // wait for balance to become available - for i := 0; i < 5; i++ { - select { - case <-time.After(500 * time.Millisecond): - balances, err := userWallet.GetBalance(ctx, true) - suite.Require().NoError(err) - totalProbi := altcurrency.BAT.FromProbi(balances.TotalProbi) - if totalProbi.GreaterThan(decimal.Zero) { - break - } - } - } - - suite.Require().True(balanceAfter.GreaterThan(balanceBefore.TotalProbi), "balance should have increased") - txn, err := userWallet.PrepareTransaction(altcurrency.BAT, altcurrency.BAT.ToProbi(order.TotalPrice), - uphold.SettlementDestination, "bat-go:grant-server.TestAC", "", nil) - suite.Require().NoError(err) - - walletID, err := uuid.FromString(userWallet.ID) - suite.Require().NoError(err) - - // create anonymous card - anonCardRequest := CreateAnonCardTransactionRequest{ - WalletID: walletID, - Transaction: txn, - } - body, err := json.Marshal(anonCardRequest) - suite.Require().NoError(err) - - req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/%s/transactions/anonymousCard", - order.ID), bytes.NewBuffer(body)) - suite.Require().NoError(err) - - rr := httptest.NewRecorder() - - server.Handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusCreated, rr.Code) - - signature, tokenPreimage, orderCreds := suite.fetchCredentials(ctx, server, order, *issuer) - - suite.Require().Equal(len(*(*[]string)(orderCreds[0].SignedCreds)), order.Items[0].Quantity) - - // Check we can retrieve the order by order and item id - r := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%s/credentials/%s", - order.ID, order.Items[0].ID), nil) - - rw := httptest.NewRecorder() - - server.Handler.ServeHTTP(rw, r) - - suite.Require().Equal(http.StatusOK, rw.Code) - - // setup vote - vote := Vote{ - Type: "auto-contribute", - Channel: "brave.com", - } - voteBytes, err := json.Marshal(vote) - suite.Require().NoError(err) - - votePayload := base64.StdEncoding.EncodeToString(voteBytes) - - suite.mockCB.EXPECT().RedeemCredentials(gomock.Any(), - []cbr.CredentialRedemption{ - { - Issuer: issuer.Name(), - TokenPreimage: tokenPreimage, - Signature: signature, - }, - }, votePayload).Return(nil) - - // perform create vote request - voteReq := VoteRequest{ - Vote: votePayload, - Credentials: []CredentialBinding{{ - PublicKey: *orderCreds[0].PublicKey, - TokenPreimage: tokenPreimage, - Signature: signature, - }}, - } - - body, err = json.Marshal(voteReq) - suite.Require().NoError(err) - - req = httptest.NewRequest(http.MethodPost, "/vote", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - rr = httptest.NewRecorder() - - server.Handler.ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code) - - <-time.After(5 * time.Second) - - voteEvent := suite.ReadKafkaVoteEvent(ctx) - - suite.Assert().Equal(vote.Type, voteEvent.Type) - suite.Assert().Equal(vote.Channel, voteEvent.Channel) - // should be number of credentials for the vote - suite.Assert().Equal(int64(len(voteReq.Credentials)), voteEvent.VoteTally) - // check that the funding source matches the issuer - suite.Assert().Equal("anonymous-card", voteEvent.FundingSource) // from SKU... -} - -func (suite *ControllersTestSuite) TestTimeLimitedCredentialsVerifyPresentation1M() { - order, _ := suite.setupCreateOrder(FreeTLTest1MSkuToken, FreeTLTest1MToken, 1) - - // setup a key for our merchant - k := suite.SetupCreateKey(order.MerchantID) - // GetKey - keyID, err := uuid.FromString(k.ID) - suite.Require().NoError(err, "error parsing key id") - - key, err := suite.service.Datastore.GetKey(keyID, false) - suite.Require().NoError(err, "error getting key from db") - secret, err := key.GetSecretKey() - suite.Require().NoError(err, "unable to decrypt secret") - - ordercreds := suite.fetchTimeLimitedCredentials(suite.service, order) - - issuerID, err := encodeIssuerID(order.MerchantID, "integration-test-free-1m") - suite.Require().NoError(err, "error attempting to encode issuer id") - - timeLimitedSecret := cryptography.NewTimeLimitedSecret([]byte(*secret)) - for _, cred := range ordercreds { - issued, err := time.Parse("2006-01-02", cred.IssuedAt) - suite.Require().NoError(err, "error attempting to parse issued at") - expires, err := time.Parse("2006-01-02", cred.ExpiresAt) - suite.Require().NoError(err, "error attempting to parse expires at") - - ok, err := timeLimitedSecret.Verify([]byte(issuerID), issued, expires, cred.Token) - suite.Require().NoError(err, "error attempting to verify time limited cred") - suite.Require().True(ok, "verify failed") - } - - var ( - lastIssued time.Time - lastExpired time.Time - ) - - var first = true - for _, cred := range ordercreds { - issued, err := time.Parse("2006-01-02", cred.IssuedAt) - suite.Require().NoError(err, "error attempting to parse issued at") - expires, err := time.Parse("2006-01-02", cred.ExpiresAt) - suite.Require().NoError(err, "error attempting to parse expires at") - - if !first { - // sometimes the first month of empty time is 1 - // validate each cred is for a different month - suite.Require().True(issued.Month() != lastIssued.Month()) - suite.Require().True(expires.Month() != lastExpired.Month()) - } - first = false - - lastIssued = issued - lastExpired = expires - } -} - -func (suite *ControllersTestSuite) TestTimeLimitedCredentialsVerifyPresentation() { - order, _ := suite.setupCreateOrder(FreeTLTestSkuToken, FreeTLTestToken, 1) - - // setup a key for our merchant - k := suite.SetupCreateKey(order.MerchantID) - // GetKey - keyID, err := uuid.FromString(k.ID) - suite.Require().NoError(err, "error parsing key id") - - key, err := suite.service.Datastore.GetKey(keyID, false) - suite.Require().NoError(err, "error getting key from db") - secret, err := key.GetSecretKey() - suite.Require().NoError(err, "unable to decrypt secret") - - ordercreds := suite.fetchTimeLimitedCredentials(suite.service, order) - - issuerID, err := encodeIssuerID(order.MerchantID, "integration-test-free") - suite.Require().NoError(err, "error attempting to encode issuer id") - - timeLimitedSecret := cryptography.NewTimeLimitedSecret([]byte(*secret)) - for _, cred := range ordercreds { - issued, err := time.Parse("2006-01-02", cred.IssuedAt) - suite.Require().NoError(err, "error attempting to parse issued at") - expires, err := time.Parse("2006-01-02", cred.ExpiresAt) - suite.Require().NoError(err, "error attempting to parse expires at") - - ok, err := timeLimitedSecret.Verify([]byte(issuerID), issued, expires, cred.Token) - suite.Require().NoError(err, "error attempting to verify time limited cred") - suite.Require().True(ok, "verify failed") - } - - var ( - lastIssued time.Time - lastExpired time.Time - ) - - var first = true - for _, cred := range ordercreds { - issued, err := time.Parse("2006-01-02", cred.IssuedAt) - suite.Require().NoError(err, "error attempting to parse issued at") - expires, err := time.Parse("2006-01-02", cred.ExpiresAt) - suite.Require().NoError(err, "error attempting to parse expires at") - - if !first { - // sometimes the first day of empty time is 1 - // validate each cred is for a different day - suite.Require().True(issued.Day() != lastIssued.Day()) - suite.Require().True(expires.Day() != lastExpired.Day()) - } - first = false - - lastIssued = issued - lastExpired = expires - } -} - -/* func (suite *ControllersTestSuite) TestFailureToSignCredentialsBadPoint() -This test is no longer valid, as we no longer are using the HTTP endpoint for signing requests, -but rather using kafka for signing request/results. No longer an applicable test. Leaving note here -as tombstone. - -*/ - -func (suite *ControllersTestSuite) SetupCreateKey(merchantID string) Key { - createRequest := &CreateKeyRequest{ - Name: "BAT-GO", - } - - body, err := json.Marshal(&createRequest) - suite.Require().NoError(err) - req, err := http.NewRequest("POST", "/v1/merchants/{merchantID}/key", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - createAPIHandler := CreateKey(suite.service) - rctx := chi.NewRouteContext() - //rctx.URLParams.Add("merchantID", "48dc25ed-4121-44ef-8147-4416a76201f7") - rctx.URLParams.Add("merchantID", merchantID) - postReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - createAPIHandler.ServeHTTP(rr, postReq) - - suite.Assert().Equal(http.StatusOK, rr.Code) - - var key Key - err = json.Unmarshal(rr.Body.Bytes(), &key) - suite.Assert().NoError(err) - - return key -} - -func (suite *ControllersTestSuite) SetupDeleteKey(key Key) Key { - deleteRequest := &DeleteKeyRequest{ - DelaySeconds: 0, - } - - body, err := json.Marshal(&deleteRequest) - suite.Require().NoError(err) - - req, err := http.NewRequest("DELETE", "/v1/merchants/id/key/{id}", bytes.NewBuffer(body)) - suite.Require().NoError(err) - - deleteAPIHandler := DeleteKey(suite.service) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("id", key.ID) - deleteReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - deleteAPIHandler.ServeHTTP(rr, deleteReq) - suite.Assert().Equal(http.StatusOK, rr.Code) - - var deletedKey Key - err = json.Unmarshal(rr.Body.Bytes(), &deletedKey) - suite.Assert().NoError(err) - - return deletedKey -} - -func (suite *ControllersTestSuite) TestCreateKey() { - Key := suite.SetupCreateKey("48dc25ed-4121-44ef-8147-4416a76201f7") - suite.Assert().Equal("48dc25ed-4121-44ef-8147-4416a76201f7", Key.Merchant) -} - -func (suite *ControllersTestSuite) TestDeleteKey() { - key := suite.SetupCreateKey("48dc25ed-4121-44ef-8147-4416a76201f7") - - deleteTime := time.Now() - deletedKey := suite.SetupDeleteKey(key) - // Ensure the expiry is within 5 seconds of when we made the call - suite.Assert().WithinDuration(deleteTime, *deletedKey.Expiry, 5*time.Second) -} - -func (suite *ControllersTestSuite) TestGetKeys() { - pg, err := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.Require().NoError(err, "Failed to get postgres conn") - - // Delete transactions so we don't run into any validation errors - _, err = pg.RawDB().Exec("DELETE FROM api_keys;") - suite.Require().NoError(err) - - key := suite.SetupCreateKey("48dc25ed-4121-44ef-8147-4416a76201f7") - - req, err := http.NewRequest("GET", "/v1/merchant/{merchantID}/keys", nil) - suite.Require().NoError(err) - - getAPIHandler := GetKeys(suite.service) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("merchantID", key.Merchant) - getReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - getAPIHandler.ServeHTTP(rr, getReq) - - suite.Assert().Equal(http.StatusOK, rr.Code) - - var keys []Key - err = json.Unmarshal(rr.Body.Bytes(), &keys) - suite.Assert().NoError(err) - - suite.Assert().Equal(1, len(keys)) -} - -func (suite *ControllersTestSuite) TestGetKeysFiltered() { - pg, err := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.Require().NoError(err, "Failed to get postgres conn") - - // Delete transactions so we don't run into any validation errors - _, err = pg.RawDB().Exec("DELETE FROM api_keys;") - suite.Require().NoError(err) - - key := suite.SetupCreateKey("48dc25ed-4121-44ef-8147-4416a76201f7") - toDelete := suite.SetupCreateKey("48dc25ed-4121-44ef-8147-4416a76201f7") - suite.SetupDeleteKey(toDelete) - - req, err := http.NewRequest("GET", "/v1/merchant/{merchantID}/keys?expired=true", nil) - suite.Require().NoError(err) - - getAPIHandler := GetKeys(suite.service) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("merchantID", key.Merchant) - getReq := req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - getAPIHandler.ServeHTTP(rr, getReq) - - suite.Assert().Equal(http.StatusOK, rr.Code) - - var keys []Key - err = json.Unmarshal(rr.Body.Bytes(), &keys) - suite.Assert().NoError(err) - - suite.Assert().Equal(2, len(keys)) -} - -func (suite *ControllersTestSuite) TestExpiredTimeLimitedCred() { - ctx := context.Background() - valid := 1 * time.Second - lastPaid := time.Now().Add(-1 * time.Minute) - expiresAt := lastPaid.Add(valid) - - order := &Order{ - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, String: "brave.com", - }, - }, - Status: OrderStatusPaid, LastPaidAt: &lastPaid, - ExpiresAt: &expiresAt, - ValidFor: &valid, - } - - creds, status, err := suite.service.GetTimeLimitedCreds(ctx, order, uuid.Nil, uuid.Nil) - suite.Require().True(creds == nil, "should not get creds back") - suite.Require().True(status == http.StatusBadRequest, "should not get creds back") - suite.Require().Error(err, "should get an error") -} - -func (suite *ControllersTestSuite) ReadKafkaVoteEvent(ctx context.Context) VoteEvent { - kafkaVoteReader, err := kafkautils.NewKafkaReader(ctx, test.RandomString(), voteTopic) - suite.Require().NoError(err) - - msg, err := kafkaVoteReader.ReadMessage(context.Background()) - suite.Require().NoError(err) - - codec := suite.service.codecs["vote"] - - native, _, err := codec.NativeFromBinary(msg.Value) - suite.Require().NoError(err) - - textual, err := codec.TextualFromNative(nil, native) - suite.Require().NoError(err) - - var voteEvent VoteEvent - err = json.Unmarshal(textual, &voteEvent) - suite.Require().NoError(err) - - return voteEvent -} - -// This test performs a full e2e test using challenge bypass server to sign use order credentials. -// It uses three tokens and expects three tokens and three signed creds to be returned. -func (suite *ControllersTestSuite) TestE2E_CreateOrderCreds_StoreSignedOrderCredentials_SingleUse() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - env := os.Getenv("ENV") - ctx = context.WithValue(ctx, appctx.EnvironmentCTXKey, env) - - // setup kafka - kafkaUnsignedOrderCredsTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC") - kafkaSignedOrderCredsDLQTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC_DLQ") - kafkaSignedOrderCredsTopic = os.Getenv("GRANT_CBP_SIGN_PRODUCER_TOPIC") - kafkaSignedRequestReaderGroupID = test.RandomString() - ctx = skustest.SetupKafka(ctx, suite.T(), kafkaUnsignedOrderCredsTopic, - kafkaSignedOrderCredsDLQTopic, kafkaSignedOrderCredsTopic) - - // create macaroon token for sku and whitelist - ctx = context.WithValue(ctx, appctx.WhitelistSKUsCTXKey, []string{FreeTestSkuToken}) - - // create order with order items - request := model.CreateOrderRequest{ - Email: test.RandomString(), - Items: []model.OrderItemRequest{ - { - SKU: FreeTestSkuToken, - Quantity: 3, - }, - }, - } - - client, err := cbr.New() - suite.Require().NoError(err) - - retryPolicy = retrypolicy.NoRetry // set this so we fail fast - - service := &Service{ - issuerRepo: repository.NewIssuer(), - Datastore: suite.storage, - cbClient: client, - retry: backoff.Retry, - } - - order, err := service.CreateOrderFromRequest(ctx, request) - suite.Require().NoError(err) - - // Create order credentials for the newly create order - data := CreateOrderCredsRequest{ - ItemID: order.Items[0].ID, - // these are already base64 encoded - BlindedCreds: []string{"HLLrM7uBm4gVWr8Bsgx3M/yxDHVJX3gNow8Sx6sAPAY=", - "Hi1j/9Pen5vRvGSLn6eZCxgtkgZX7LU9edmOD2w5CWo=", "YG07TqExOSoo/46SIWK42OG0of3z94Y5SzCswW6sYSw="}, - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - requestID := uuid.NewV4().String() - ctx = context.WithValue(ctx, requestutils.RequestID, requestID) - - r := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/%s/credentials", - order.ID), bytes.NewBuffer(payload)).WithContext(ctx) - - rw := httptest.NewRecorder() - - // Enable store signed order creds consumer - ctx = context.WithValue(ctx, appctx.SkusEnableStoreSignedOrderCredsConsumer, true) - ctx = context.WithValue(ctx, appctx.SkusNumberStoreSignedOrderCredsConsumer, 1) - - skuService, err := InitService(ctx, suite.storage, nil, repository.NewOrder(), repository.NewIssuer()) - suite.Require().NoError(err) - - authMwr := NewAuthMwr(skuService) - instrumentHandler := func(name string, h http.Handler) http.Handler { - return h - } - - router := Router(skuService, authMwr, instrumentHandler, newCORSOptsEnv()) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, r) - suite.Require().Equal(http.StatusOK, rw.Code) - - // Assert the order creds have been submitted - messages, err := suite.storage.GetSigningOrderRequestOutboxByOrder(ctx, order.ID) - suite.Require().NoError(err) - suite.Require().Len(messages, 1) - suite.Require().Equal(data.ItemID, messages[0].ItemID) - - // start processing out signing request/results from kafka - go func() { - for { - select { - case <-ctx.Done(): - break - default: - skuService.RunStoreSignedOrderCredentials(ctx, 0) - } - } - }() - - go func() { - for { - select { - case <-ctx.Done(): - break - default: - _, _ = skuService.RunSendSigningRequestJob(ctx) - time.Sleep(time.Second) - } - } - }() - - var recorder *httptest.ResponseRecorder - req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%s/credentials", order.ID), nil) - timeout := time.Now().Add(time.Second * 50) - - for { - recorder = httptest.NewRecorder() - server.Handler.ServeHTTP(recorder, req) - if recorder.Code != http.StatusAccepted || time.Now().After(timeout) { - break - } - time.Sleep(1 * time.Second) - } - - suite.Require().Equal(http.StatusOK, recorder.Code) - - var response []OrderCreds - err = json.NewDecoder(recorder.Body).Decode(&response) - suite.Require().NoError(err) - - suite.Assert().Equal(order.ID, response[0].OrderID) - suite.Assert().NotEmpty(response[0].IssuerID) - suite.Assert().Equal(1, len(response)) - suite.Assert().NotEmpty(response[0].SignedCreds) -} - -// This test performs a full e2e test using challenge bypass server to sign time limited v2 order credentials. -// It uses three tokens and expects three signing results (which is determined by the issuer buffer/overlap and CBR) -// which translates to three time limited v2 order credentials being stored for the single order containing -// a single order item. -func (suite *ControllersTestSuite) TestE2E_CreateOrderCreds_StoreSignedOrderCredentials_TimeLimitedV2() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - env := os.Getenv("ENV") - ctx = context.WithValue(ctx, appctx.EnvironmentCTXKey, env) - - // setup kafka - kafkaUnsignedOrderCredsTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC") - kafkaSignedOrderCredsDLQTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC_DLQ") - kafkaSignedOrderCredsTopic = os.Getenv("GRANT_CBP_SIGN_PRODUCER_TOPIC") - kafkaSignedRequestReaderGroupID = test.RandomString() - ctx = skustest.SetupKafka(ctx, suite.T(), kafkaUnsignedOrderCredsTopic, - kafkaSignedOrderCredsDLQTopic, kafkaSignedOrderCredsTopic) - - // create macaroon token for sku and whitelist - sku := test.RandomString() - price := 0 - token := suite.CreateMacaroon(sku, price) - ctx = context.WithValue(ctx, appctx.WhitelistSKUsCTXKey, []string{token}) - - // create order with order items - request := model.CreateOrderRequest{ - Email: test.RandomString(), - Items: []model.OrderItemRequest{ - { - SKU: token, - Quantity: 1, - }, - }, - } - - client, err := cbr.New() - suite.Require().NoError(err) - - retryPolicy = retrypolicy.NoRetry // set this so we fail fast - - service := &Service{ - issuerRepo: repository.NewIssuer(), - Datastore: suite.storage, - cbClient: client, - retry: backoff.Retry, - } - - order, err := service.CreateOrderFromRequest(ctx, request) - suite.Require().NoError(err) - - err = service.Datastore.UpdateOrder(order.ID, OrderStatusPaid) // to update the last paid at - suite.Require().NoError(err) - - // Create order credentials for the newly create order - data := CreateOrderCredsRequest{ - ItemID: order.Items[0].ID, - // these are already base64 encoded - BlindedCreds: []string{"HLLrM7uBm4gVWr8Bsgx3M/yxDHVJX3gNow8Sx6sAPAY=", - "Hi1j/9Pen5vRvGSLn6eZCxgtkgZX7LU9edmOD2w5CWo=", "YG07TqExOSoo/46SIWK42OG0of3z94Y5SzCswW6sYSw="}, - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - requestID := uuid.NewV4().String() - ctx = context.WithValue(ctx, requestutils.RequestID, requestID) - - r := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/%s/credentials", - order.ID), bytes.NewBuffer(payload)).WithContext(ctx) - - rw := httptest.NewRecorder() - - // Enable store signed order creds consumer - ctx = context.WithValue(ctx, appctx.SkusEnableStoreSignedOrderCredsConsumer, true) - ctx = context.WithValue(ctx, appctx.SkusNumberStoreSignedOrderCredsConsumer, 1) - - skuService, err := InitService(ctx, suite.storage, nil, repository.NewOrder(), repository.NewIssuer()) - suite.Require().NoError(err) - - authMwr := NewAuthMwr(skuService) - instrumentHandler := func(name string, h http.Handler) http.Handler { - return h - } - - router := Router(skuService, authMwr, instrumentHandler, newCORSOptsEnv()) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, r) - suite.Require().Equal(http.StatusOK, rw.Code) - - // Assert the order creds have been submitted - messages, err := suite.storage.GetSigningOrderRequestOutboxByOrder(ctx, order.ID) - suite.Require().NoError(err) - suite.Require().Len(messages, 1) - suite.Require().Equal(data.ItemID, messages[0].ItemID) - - // start processing out signing request/results from kafka - go func() { - for { - select { - case <-ctx.Done(): - break - default: - skuService.RunStoreSignedOrderCredentials(ctx, 0) - } - } - }() - - go func() { - for { - select { - case <-ctx.Done(): - break - default: - _, _ = skuService.RunSendSigningRequestJob(ctx) - time.Sleep(time.Second) - } - } - }() - - var recorder *httptest.ResponseRecorder - req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%s/credentials", order.ID), nil) - timeout := time.Now().Add(time.Second * 50) - - for { - recorder = httptest.NewRecorder() - server.Handler.ServeHTTP(recorder, req) - if recorder.Code != http.StatusAccepted || time.Now().After(timeout) { - break - } - time.Sleep(1 * time.Second) - } - - suite.Require().Equal(http.StatusOK, recorder.Code) - - var response []TimeAwareSubIssuedCreds - err = json.NewDecoder(recorder.Body).Decode(&response) - suite.Require().NoError(err) - - suite.Assert().Equal(order.ID, response[0].OrderID) - suite.Assert().NotEmpty(response[0].IssuerID) - suite.Assert().Equal(3, len(response)) -} - -func (suite *ControllersTestSuite) TestCreateOrderCreds_SingleUse_ExistingOrderCredentials() { - ctx := context.Background() - defer ctx.Done() - - // create macaroon token for sku and whitelist - ctx = context.WithValue(ctx, appctx.WhitelistSKUsCTXKey, []string{FreeTestSkuToken}) - - // create order with order items - request := model.CreateOrderRequest{ - Email: test.RandomString(), - Items: []model.OrderItemRequest{ - { - SKU: FreeTestSkuToken, - Quantity: 3, - }, - }, - } - - client, err := cbr.New() - suite.Require().NoError(err) - - retryPolicy = retrypolicy.NoRetry // set this so we fail fast - - service := &Service{ - issuerRepo: repository.NewIssuer(), - Datastore: suite.storage, - cbClient: client, - retry: backoff.Retry, - } - - order, err := service.CreateOrderFromRequest(ctx, request) - suite.Require().NoError(err) - - // create and send the order credentials request for the newly create order - data := CreateOrderCredsRequest{ - ItemID: order.Items[0].ID, - // these are already base64 encoded - BlindedCreds: []string{"HLLrM7uBm4gVWr8Bsgx3M/yxDHVJX3gNow8Sx6sAPAY=", - "Hi1j/9Pen5vRvGSLn6eZCxgtkgZX7LU9edmOD2w5CWo=", "YG07TqExOSoo/46SIWK42OG0of3z94Y5SzCswW6sYSw="}, - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - requestID := uuid.NewV4().String() - ctx = context.WithValue(ctx, requestutils.RequestID, requestID) - - r := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/%s/credentials", - order.ID), bytes.NewBuffer(payload)).WithContext(ctx) - - rw := httptest.NewRecorder() - - authMwr := NewAuthMwr(service) - instrumentHandler := func(name string, h http.Handler) http.Handler { - return h - } - - router := Router(service, authMwr, instrumentHandler, newCORSOptsEnv()) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, r) - suite.Require().Equal(http.StatusOK, rw.Code) - - // Send a second create order request for the same order and order item - rw = httptest.NewRecorder() - r = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/%s/credentials", - order.ID), bytes.NewBuffer(payload)).WithContext(ctx) - - server.Handler.ServeHTTP(rw, r) - suite.Assert().Equal(http.StatusBadRequest, rw.Code) - - var appError handlers.AppError - err = json.NewDecoder(rw.Body).Decode(&appError) - suite.Require().NoError(err) - - suite.Assert().Equal(http.StatusBadRequest, appError.Code) - suite.Assert().Contains(appError.Error(), ErrCredsAlreadyExist.Error()) -} - -// ReadSigningOrderRequestMessage reads messages from the unsigned order request topic -func (suite *ControllersTestSuite) ReadSigningOrderRequestMessage(ctx context.Context, topic string) SigningOrderRequest { - kafkaReader, err := kafkautils.NewKafkaReader(ctx, test.RandomString(), topic) - suite.Require().NoError(err) - - msg, err := kafkaReader.ReadMessage(ctx) - suite.Require().NoError(err) - - codec, err := goavro.NewCodec(signingOrderRequestSchema) - suite.Require().NoError(err) - - native, _, err := codec.NativeFromBinary(msg.Value) - suite.Require().NoError(err) - - textual, err := codec.TextualFromNative(nil, native) - suite.Require().NoError(err) - - var signingOrderRequest SigningOrderRequest - err = json.Unmarshal(textual, &signingOrderRequest) - suite.Require().NoError(err) - - return signingOrderRequest -} - -// To create an unpaid order item set price to 0 -func (suite *ControllersTestSuite) CreateMacaroon(sku string, price int) string { - c := macaroon.Caveats{ - "sku": sku, - "price": strconv.Itoa(price), - "description": test.RandomString(), - "currency": "usd", - "credential_type": "time-limited-v2", - "credential_valid_duration": "P1M", - "each_credential_valid_duration": "P1D", - "issuer_token_buffer": strconv.Itoa(3), - "issuer_token_overlap": strconv.Itoa(0), - "allowed_payment_methods": test.RandomString(), - "metadata": ` - { - "stripe_product_id":"stripe_product_id", - "stripe_success_url":"stripe_success_url", - "stripe_cancel_url":"stripe_cancel_url" - } - `, - } - - t := macaroon.Token{ - ID: test.RandomString(), Version: 1, Location: "brave.com", - FirstPartyCaveats: []macaroon.Caveats{c}, - } - - mac, err := t.Generate("secret") - suite.Require().NoError(err) - - skuMap["development"][mac] = true - - return mac -} - -func newCORSOptsEnv() cors.Options { - origins := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",") - dbg, _ := strconv.ParseBool(os.Getenv("DEBUG")) - - return NewCORSOpts(origins, dbg) -} diff --git a/services/skus/credentials.go b/services/skus/credentials.go deleted file mode 100644 index c17a8b1a2..000000000 --- a/services/skus/credentials.go +++ /dev/null @@ -1,786 +0,0 @@ -package skus - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/url" - "time" - - "github.com/jmoiron/sqlx" - "github.com/linkedin/goavro" - uuid "github.com/satori/go.uuid" - kafka "github.com/segmentio/kafka-go" - - "github.com/brave-intl/bat-go/libs/backoff/retrypolicy" - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/cbr" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/jsonutils" - "github.com/brave-intl/bat-go/libs/ptr" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -const ( - defaultMaxTokensPerIssuer = 4000000 // ~1M BAT - defaultCohort int16 = 1 -) - -var ( - ErrOrderUnpaid = errors.New("order not paid") - ErrOrderHasNoItems model.Error = "order has no items" - ErrCredsAlreadyExist model.Error = "credentials already exist" - - errInvalidIssuerResp model.Error = "invalid issuer response" - errInvalidNCredsSingleUse model.Error = "submitted more blinded creds than quantity of order item" - errInvalidNCredsTlv2 model.Error = "submitted more blinded creds than allowed for order" - errUnsupportedCredType model.Error = "unsupported credential type" - errItemDoesNotExist model.Error = "order item does not exist for order" - errCredsAlreadySubmitted model.Error = "credentials already submitted" - - defaultExpiresAt = time.Now().Add(17532 * time.Hour) // 2 years - retryPolicy = retrypolicy.DefaultRetry - dontRetryCodes = map[int]struct{}{ - http.StatusBadRequest: struct{}{}, - http.StatusUnauthorized: struct{}{}, - http.StatusForbidden: struct{}{}, - http.StatusInternalServerError: struct{}{}, - http.StatusConflict: struct{}{}, - } -) - -// CreateIssuer creates a new v1 issuer if it does not exist. -// -// This only happens in the event of a new sku being created. -func (s *Service) CreateIssuer(ctx context.Context, dbi sqlx.QueryerContext, merchID string, item *OrderItem) error { - encMerchID, err := encodeIssuerID(merchID, item.SKU) - if err != nil { - return errorutils.Wrap(err, "error encoding issuer name") - } - - _, err = s.issuerRepo.GetByMerchID(ctx, dbi, encMerchID) - // Found, nothing to do. - if err == nil { - return nil - } - - if !errors.Is(err, model.ErrIssuerNotFound) { - return fmt.Errorf("error get issuer for issuerID %s: %w", encMerchID, err) - } - - reqFn := func() (interface{}, error) { - return nil, s.cbClient.CreateIssuer(ctx, encMerchID, defaultMaxTokensPerIssuer) - } - - // The create issuer endpoint returns a conflict if the issuer already exists. - _, err = s.retry(ctx, reqFn, retryPolicy, canRetry(dontRetryCodes)) - if err != nil && !isConflict(err) { - return fmt.Errorf("error calling cbr create issuer: %w", err) - } - - reqFn = func() (interface{}, error) { - return s.cbClient.GetIssuer(ctx, encMerchID) - } - - resp, err := s.retry(ctx, reqFn, retryPolicy, canRetry(dontRetryCodes)) - if err != nil { - return fmt.Errorf("error getting issuer %s: %w", encMerchID, err) - } - - issuerResp, ok := resp.(*cbr.IssuerResponse) - if !ok { - return errInvalidIssuerResp - } - - if _, err := s.issuerRepo.Create(ctx, dbi, model.IssuerNew{ - MerchantID: issuerResp.Name, - PublicKey: issuerResp.PublicKey, - }); err != nil { - return fmt.Errorf("error creating new issuer: %w", err) - } - - return nil -} - -// CreateIssuerV3 creates a new v3 issuer if it does not exist. -// -// This only happens in the event of a new sku being created. -func (s *Service) CreateIssuerV3(ctx context.Context, dbi sqlx.QueryerContext, merchID string, item *OrderItem, issuerCfg model.IssuerConfig) error { - encMerchID, err := encodeIssuerID(merchID, item.SKU) - if err != nil { - return errorutils.Wrap(err, "error encoding issuer name") - } - - _, err = s.issuerRepo.GetByMerchID(ctx, dbi, encMerchID) - // Found, nothing to do. - if err == nil { - return nil - } - - if !errors.Is(err, model.ErrIssuerNotFound) { - return fmt.Errorf("error get issuer for issuerID %s: %w", encMerchID, err) - } - - if item.EachCredentialValidForISO == nil { - return fmt.Errorf("error each credential valid iso is empty for order item sku %s", item.SKU) - } - - req := cbr.IssuerRequest{ - Name: encMerchID, - Cohort: defaultCohort, - MaxTokens: defaultMaxTokensPerIssuer, - ValidFrom: ptr.FromTime(time.Now()), - ExpiresAt: ptr.FromTime(defaultExpiresAt), - Duration: *item.EachCredentialValidForISO, - Buffer: issuerCfg.Buffer, - Overlap: issuerCfg.Overlap, - } - - reqFn := func() (interface{}, error) { - return nil, s.cbClient.CreateIssuerV3(ctx, req) - } - - // The create issuer v3 endpoints returns a conflict if the issuer already exists. - _, err = s.retry(ctx, reqFn, retryPolicy, canRetry(dontRetryCodes)) - if err != nil && !isConflict(err) { - return fmt.Errorf("error calling cbr create issuer v3: %w", err) - } - - reqFn = func() (interface{}, error) { - return s.cbClient.GetIssuerV3(ctx, req.Name) - } - - resp, err := s.retry(ctx, reqFn, retryPolicy, canRetry(dontRetryCodes)) - if err != nil { - return fmt.Errorf("error getting issuer %s: %w", req.Name, err) - } - - issuerResp, ok := resp.(*cbr.IssuerResponse) - if !ok { - return errInvalidIssuerResp - } - - if _, err := s.issuerRepo.Create(ctx, dbi, model.IssuerNew{ - MerchantID: issuerResp.Name, - PublicKey: issuerResp.PublicKey, - }); err != nil { - return fmt.Errorf("error creating new issuer: %w", err) - } - - return nil -} - -func canRetry(nonRetrySet map[int]struct{}) func(error) bool { - return func(err error) bool { - var eb *errorutils.ErrorBundle - switch { - case errors.As(err, &eb): - if state, ok := eb.Data().(clients.HTTPState); ok { - if _, ok := nonRetrySet[state.Status]; ok { - return false - } - - return true - } - } - - return false - } -} - -func isConflict(err error) bool { - var eb *errorutils.ErrorBundle - if errors.As(err, &eb) { - if httpState, ok := eb.Data().(clients.HTTPState); ok { - return httpState.Status == http.StatusConflict - } - } - - return false -} - -// OrderCreds encapsulates the credentials to be signed in response to a completed order -type OrderCreds struct { - ID uuid.UUID `json:"id" db:"item_id"` - OrderID uuid.UUID `json:"orderId" db:"order_id"` - IssuerID uuid.UUID `json:"issuerId" db:"issuer_id"` - BlindedCreds jsonutils.JSONStringArray `json:"blindedCreds" db:"blinded_creds"` - SignedCreds *jsonutils.JSONStringArray `json:"signedCreds" db:"signed_creds"` - BatchProof *string `json:"batchProof" db:"batch_proof"` - PublicKey *string `json:"publicKey" db:"public_key"` -} - -// TimeLimitedCreds encapsulates time-limited credentials -type TimeLimitedCreds struct { - ID uuid.UUID `json:"id"` - OrderID uuid.UUID `json:"orderId"` - IssuedAt string `json:"issuedAt"` - ExpiresAt string `json:"expiresAt"` - Token string `json:"token"` -} - -// CreateOrderItemCredentials creates credentials for the given order id and item with the supplied blinded credentials. -// -// It handles only paid orders. -func (s *Service) CreateOrderItemCredentials(ctx context.Context, orderID, itemID, requestID uuid.UUID, blindedCreds []string) error { - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return fmt.Errorf("error retrieving order: %w", err) - } - - if order == nil { - return fmt.Errorf("error retrieving orderID %s: %w", orderID, errorutils.ErrNotFound) - } - - if !order.IsPaid() { - return ErrOrderUnpaid - } - - var orderItem *OrderItem - for _, item := range order.Items { - if item.ID == itemID { - orderItem = &item - break - } - } - - if orderItem == nil { - return errItemDoesNotExist - } - - if err := s.doCredentialsExist(ctx, orderItem, blindedCreds); err != nil { - if errors.Is(err, errCredsAlreadySubmitted) { - return nil - } - - return err - } - - if err := checkNumBlindedCreds(order, orderItem, len(blindedCreds)); err != nil { - return err - } - - issuerID, err := encodeIssuerID(order.MerchantID, orderItem.SKU) - if err != nil { - return errorutils.Wrap(err, "error encoding issuer name") - } - - issuer, err := s.issuerRepo.GetByMerchID(ctx, s.Datastore.RawDB(), issuerID) - if err != nil { - return fmt.Errorf("error getting issuer for issuerID %s: %w", issuerID, err) - } - - metadata := &Metadata{ - ItemID: orderItem.ID, - OrderID: order.ID, - IssuerID: issuer.ID, - CredentialType: orderItem.CredentialType, - } - - associatedData, err := json.Marshal(metadata) - if err != nil { - return fmt.Errorf("error serializing associated data: %w", err) - } - - signReq := SigningOrderRequest{ - RequestID: requestID.String(), - Data: []SigningOrder{ - { - IssuerType: issuerID, - IssuerCohort: defaultCohort, - BlindedTokens: blindedCreds, - AssociatedData: associatedData, - }, - }, - } - - if err := s.Datastore.InsertSigningOrderRequestOutbox(ctx, requestID, order.ID, orderItem.ID, signReq); err != nil { - return fmt.Errorf("error inserting signing order request outbox orderID %s: %w", order.ID, err) - } - - return nil -} - -func (s *Service) doCredentialsExist(ctx context.Context, item *model.OrderItem, blindedCreds []string) error { - switch item.CredentialType { - case timeLimitedV2: - // NOTE: This creates a possible race to submit between clients. - // Multiple signing request outboxes can be created since their - // uniqueness constraint is on the request id. - // Despite this, the uniqueness constraint of time_limited_v2_order_creds ensures that - // only one set of credentials is written for each order / item & time interval. - // As a result, one client will successfully unblind the credentials and - // the others will fail. - - return s.doTLV2Exist(ctx, item, blindedCreds) - default: - return s.doCredsExist(ctx, item) - } -} - -func (s *Service) doTLV2Exist(ctx context.Context, item *model.OrderItem, blindedCreds []string) error { - if item.CredentialType != timeLimitedV2 { - return errUnsupportedCredType - } - - // Check TLV2 to see if we have credentials signed that match incoming blinded tokens. - alreadySubmitted, err := s.Datastore.AreTimeLimitedV2CredsSubmitted(ctx, blindedCreds...) - if err != nil { - return fmt.Errorf("error validating credentials exist for order item: %w", err) - } - - if alreadySubmitted { - // No need to create order credentials, since these are already submitted. - return errCredsAlreadySubmitted - } - - // Check if we have signed credentials for this order item. - // If there is no order and no creds, we can submit again. - // Similar to the outbox check case, delete order creds will wipe out any already signed order creds. - creds, err := s.Datastore.GetTimeLimitedV2OrderCredsByOrderItem(item.ID) - if err != nil { - return fmt.Errorf("error validating no credentials exist for order item: %w", err) - } - - if creds != nil { - return ErrCredsAlreadyExist - } - - return nil -} - -func (s *Service) doCredsExist(ctx context.Context, item *model.OrderItem) error { - if item.CredentialType == timeLimitedV2 { - return errUnsupportedCredType - } - - // Check if we already have a signing request for this order, delete order creds will - // delete the prior signing request. - // This allows subscriptions to manage how many order creds are handed out. - signingOrderRequests, err := s.Datastore.GetSigningOrderRequestOutboxByOrderItem(ctx, item.ID) - if err != nil { - return fmt.Errorf("error validating no credentials exist for order item: %w", err) - } - - if len(signingOrderRequests) > 0 { - return ErrCredsAlreadyExist - } - - return nil -} - -// OrderWorker attempts to work on an order job by signing the blinded credentials of the client -type OrderWorker interface { - SignOrderCreds(ctx context.Context, orderID uuid.UUID, issuer Issuer, blindedCreds []string) (*OrderCreds, error) -} - -// SignOrderCreds signs the blinded credentials -func (s *Service) SignOrderCreds(ctx context.Context, orderID uuid.UUID, issuer Issuer, blindedCreds []string) (*OrderCreds, error) { - resp, err := s.cbClient.SignCredentials(ctx, issuer.Name(), blindedCreds) - if err != nil { - return nil, err - } - - signedTokens := jsonutils.JSONStringArray(resp.SignedTokens) - - creds := &OrderCreds{ - ID: orderID, - BlindedCreds: blindedCreds, - SignedCreds: &signedTokens, - BatchProof: &resp.BatchProof, - PublicKey: &issuer.PublicKey, - } - - return creds, nil -} - -// SigningRequestWriter is the interface implemented by types that can write signing request messages. -type SigningRequestWriter interface { - WriteMessage(ctx context.Context, message []byte) error - WriteMessages(ctx context.Context, messages []SigningOrderRequestOutbox) error -} - -// WriteMessage writes a single message to the kafka topic configured on this writer. -func (s *Service) WriteMessage(ctx context.Context, message []byte) error { - native, _, err := s.codecs[kafkaUnsignedOrderCredsTopic].NativeFromTextual(message) - if err != nil { - return fmt.Errorf("error converting native from textual: %w", err) - } - - binary, err := s.codecs[kafkaUnsignedOrderCredsTopic].BinaryFromNative(nil, native) - if err != nil { - return fmt.Errorf("error converting binary from native: %w", err) - } - - err = s.kafkaWriter.WriteMessages(ctx, kafka.Message{ - Topic: kafkaUnsignedOrderCredsTopic, - Value: binary, - }) - if err != nil { - return fmt.Errorf("error writting kafka message: %w", err) - } - - return nil -} - -// WriteMessages writes a batch of SigningOrderRequestOutbox messages to the kafka topic configured on this writer. -func (s *Service) WriteMessages(ctx context.Context, messages []SigningOrderRequestOutbox) error { - msgs := make([]kafka.Message, len(messages)) - - for i := 0; i < len(messages); i++ { - native, _, err := s.codecs[kafkaUnsignedOrderCredsTopic].NativeFromTextual(messages[i].Message) - if err != nil { - return fmt.Errorf("error converting native from textual: %w", err) - } - - binary, err := s.codecs[kafkaUnsignedOrderCredsTopic].BinaryFromNative(nil, native) - if err != nil { - return fmt.Errorf("error converting binary from native: %w", err) - } - - km := kafka.Message{ - Topic: kafkaUnsignedOrderCredsTopic, - Key: messages[i].RequestID.Bytes(), - Value: binary, - } - - msgs[i] = km - } - - err := s.kafkaWriter.WriteMessages(ctx, msgs...) - if err != nil { - return fmt.Errorf("error writting kafka message: %w", err) - } - - return nil -} - -// Decode decodes the kafka message using from the avro schema. -func (s *Service) Decode(message kafka.Message) (*SigningOrderResult, error) { - codec, ok := s.codecs[kafkaSignedOrderCredsTopic] - if !ok { - return nil, fmt.Errorf("read message: could not find codec %s", kafkaSignedOrderCredsTopic) - } - - native, _, err := codec.NativeFromBinary(message.Value) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode naitve from binary %w", err) - } - - textual, err := codec.TextualFromNative(nil, native) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode textual from native %w", err) - } - - var signedOrderResult SigningOrderResult - err = json.Unmarshal(textual, &signedOrderResult) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode json from textual %w", err) - } - - return &signedOrderResult, nil -} - -// generateCredentialRedemptions - helper to create credential redemptions from cred bindings -var generateCredentialRedemptions = func(ctx context.Context, cb []CredentialBinding) ([]cbr.CredentialRedemption, error) { - // deduplicate credential bindings - cb = deduplicateCredentialBindings(cb...) - - var ( - requestCredentials = make([]cbr.CredentialRedemption, len(cb)) - issuers = make(map[string]*Issuer) - ) - - db, ok := ctx.Value(appctx.DatastoreCTXKey).(Datastore) - if !ok { - return nil, errors.New("failed to get datastore from context") - } - - for i := 0; i < len(cb); i++ { - - var ( - ok bool - issuer *Issuer - err error - ) - - publicKey := cb[i].PublicKey - - if issuer, ok = issuers[publicKey]; !ok { - issuer, err = db.GetIssuerByPublicKey(publicKey) - if err != nil { - return nil, fmt.Errorf("error finding issuer: %w", err) - } - } - - requestCredentials[i].Issuer = issuer.Name() - requestCredentials[i].TokenPreimage = cb[i].TokenPreimage - requestCredentials[i].Signature = cb[i].Signature - } - return requestCredentials, nil -} - -// CredentialBinding includes info needed to redeem a single credential -type CredentialBinding struct { - PublicKey string `json:"publicKey" valid:"base64"` - TokenPreimage string `json:"t" valid:"base64"` - Signature string `json:"signature" valid:"base64"` -} - -// deduplicateCredentialBindings - given a list of tokens return a deduplicated list -func deduplicateCredentialBindings(tokens ...CredentialBinding) []CredentialBinding { - var ( - seen = map[string]bool{} - result []CredentialBinding - ) - for _, t := range tokens { - if !seen[t.TokenPreimage] { - seen[t.TokenPreimage] = true - result = append(result, t) - } - } - return result -} - -func decodeIssuerID(issuerID string) (string, string, error) { - var ( - merchantID string - sku string - ) - - u, err := url.Parse(issuerID) - if err != nil { - return "", "", fmt.Errorf("parse issuer name: %w", err) - } - - sku = u.Query().Get("sku") - u.RawQuery = "" - merchantID = u.String() - - return merchantID, sku, nil -} - -func encodeIssuerID(merchantID, sku string) (string, error) { - v := url.Values{} - v.Add("sku", sku) - - u, err := url.Parse(merchantID + "?" + v.Encode()) - if err != nil { - return "", fmt.Errorf("parse merchant id: %w", err) - } - - return u.String(), nil -} - -// SignedOrderCredentialsHandler - this is the handler for getting the signed order credentials -type SignedOrderCredentialsHandler struct { - decoder Decoder - datastore Datastore -} - -// Handle processes Kafka message of type SigningOrderResult. -func (s *SignedOrderCredentialsHandler) Handle(ctx context.Context, message kafka.Message) (err error) { - signedOrderResult, err := s.decoder.Decode(message) - if err != nil { - return fmt.Errorf("error decoding message key %s partition %d offset %d: %w", - string(message.Key), message.Partition, message.Offset, err) - } - - requestID, err := uuid.FromString(signedOrderResult.RequestID) - if err != nil { - return fmt.Errorf("error getting uuid from signed order request %w", err) - } - - ctx, tx, rollback, commit, err := datastore.GetTx(ctx, s.datastore) - defer rollback() - - // Check to see if the signing request has not been deleted whilst signing the request. - sor, err := s.datastore.GetSigningOrderRequestOutboxByRequestID(ctx, tx, requestID) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - return fmt.Errorf("error get signing order credentials tx: %w", err) - } - - if sor == nil || sor.CompletedAt != nil { - return nil - } - - err = s.datastore.InsertSignedOrderCredentialsTx(ctx, tx, signedOrderResult) - if err != nil { - return fmt.Errorf("error inserting signed order credentials: %w", err) - } - - err = s.datastore.UpdateSigningOrderRequestOutboxTx(ctx, tx, requestID, time.Now()) - if err != nil { - return fmt.Errorf("error updating signing order request outbox: %w", err) - } - - err = commit() - if err != nil { - return fmt.Errorf("error commiting signing order request outbox: %w", err) - } - - return nil -} - -// Decoder - kafka message decoder interface -type Decoder interface { - Decode(message kafka.Message) (*SigningOrderResult, error) -} - -// SigningOrderResultDecoder - signed order result kafka message decoder interface -type SigningOrderResultDecoder struct { - codec *goavro.Codec -} - -// Decode decodes the kafka message using from the avro schema. -func (s *SigningOrderResultDecoder) Decode(message kafka.Message) (*SigningOrderResult, error) { - native, _, err := s.codec.NativeFromBinary(message.Value) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode naitve from binary %w", err) - } - - textual, err := s.codec.TextualFromNative(nil, native) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode textual from native %w", err) - } - - var signedOrderResult SigningOrderResult - err = json.Unmarshal(textual, &signedOrderResult) - if err != nil { - return nil, fmt.Errorf("read message: error could not decode json from textual %w", err) - } - - return &signedOrderResult, nil -} - -// SigningOrderResultErrorHandler - error handler for signing results -type SigningOrderResultErrorHandler struct { - kafkaWriter *kafka.Writer -} - -// Handle writes messages the SigningResultReader's dead letter queue. -func (s *SigningOrderResultErrorHandler) Handle(ctx context.Context, message kafka.Message, errorMessage error) error { - message.Headers = append(message.Headers, kafka.Header{ - Key: "error-message", - Value: []byte(errorMessage.Error()), - }) - message.Topic = kafkaSignedOrderCredsDLQTopic - err := s.kafkaWriter.WriteMessages(ctx, message) - if err != nil { - return fmt.Errorf("error writting message to signing result dlq: %w", err) - } - return nil -} - -// DeleteOrderCreds hard-deletes all the order credentials associated with the given orderID. -// -// This includes both time-limited-v2 and single-use credentials. -// The isSigned param only applies to single use and will always be false for time-limited-v2. -// Credentials cannot be deleted when an order is in the process of being signed. -// -// TODO(pavelb): -// - create repos for credentials; -// - move the corresponding methods there; -// - make those methods work on per-item basis. -func (s *Service) DeleteOrderCreds(ctx context.Context, orderID uuid.UUID, isSigned bool) error { - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return err - } - - if len(order.Items) == 0 { - return ErrOrderHasNoItems - } - - doSingleUse, doTlv2 := doItemsHaveSUOrTlv2(order.Items) - - // Handle special cases: - // - 1 item with time-limited credential type; - // - multiple items with time-limited credential type. - if !doSingleUse && !doTlv2 { - return nil - } - - tx, err := s.Datastore.RawDB().BeginTxx(ctx, nil) - if err != nil { - return err - } - defer s.Datastore.RollbackTx(tx) - - if doSingleUse { - if err := s.Datastore.DeleteSingleUseOrderCredsByOrderTx(ctx, tx, orderID, isSigned); err != nil { - return fmt.Errorf("error deleting single use order creds: %w", err) - } - } - - if doTlv2 { - if err := s.Datastore.DeleteTimeLimitedV2OrderCredsByOrderTx(ctx, tx, orderID); err != nil { - return fmt.Errorf("error deleting time limited v2 order creds: %w", err) - } - } - - if err := s.Datastore.DeleteSigningOrderRequestOutboxByOrderTx(ctx, tx, orderID); err != nil { - return fmt.Errorf("error deleting order creds signing in progress") - } - - if err := tx.Commit(); err != nil { - return fmt.Errorf("error commiting delete order creds: %w", err) - } - - return nil -} - -// checkNumBlindedCreds checks the number of submitted blinded credentials. -// -// The number of submitted credentials must not exceed: -// - for single-use the quantity of the item; -// - for time-limited-v2 the product of numPerInterval and numIntervals. -func checkNumBlindedCreds(ord *model.Order, item *model.OrderItem, ncreds int) error { - switch item.CredentialType { - case singleUse: - if ncreds > item.Quantity { - return errInvalidNCredsSingleUse - } - - return nil - case timeLimitedV2: - nperInterval, err := ord.NumPerInterval() - if err != nil { - return err - } - - nintervals, err := ord.NumIntervals() - if err != nil { - return err - } - - if ncreds > nperInterval*nintervals { - return errInvalidNCredsTlv2 - } - - return nil - default: - return nil - } -} - -func doItemsHaveSUOrTlv2(items []model.OrderItem) (bool, bool) { - var hasSingleUse, hasTlv2 bool - - for i := range items { - switch items[i].CredentialType { - case singleUse: - hasSingleUse = true - case timeLimitedV2: - hasTlv2 = true - } - } - - return hasSingleUse, hasTlv2 -} diff --git a/services/skus/credentials_test.go b/services/skus/credentials_test.go deleted file mode 100644 index 0bff8aa04..000000000 --- a/services/skus/credentials_test.go +++ /dev/null @@ -1,659 +0,0 @@ -//go:build integration - -package skus - -import ( - "context" - "encoding/json" - "errors" - "net/http" - "sync" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/jmoiron/sqlx" - "github.com/linkedin/goavro" - uuid "github.com/satori/go.uuid" - "github.com/segmentio/kafka-go" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/cbr" - mock_cbr "github.com/brave-intl/bat-go/libs/clients/cbr/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/ptr" - "github.com/brave-intl/bat-go/libs/test" - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/skustest" - - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -type CredentialsTestSuite struct { - suite.Suite - storage Datastore -} - -func TestCredentialsTestSuite(t *testing.T) { - suite.Run(t, new(CredentialsTestSuite)) -} - -func (suite *CredentialsTestSuite) SetupSuite() { - skustest.Migrate(suite.T()) - storage, _ := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.storage = storage -} - -func (suite *CredentialsTestSuite) AfterTest() { - skustest.CleanDB(suite.T(), suite.storage.RawDB()) -} - -func (suite *CredentialsTestSuite) TestSignedOrderCredentialsHandler_KafkaDuplicates() { - // Create an issuer and a paid order with one order item and a time limited v2 credential type. - ctx := context.WithValue(context.Background(), appctx.WhitelistSKUsCTXKey, []string{devFreeTimeLimitedV2}) - order, issuer := createOrderAndIssuer(suite.T(), ctx, suite.storage, devFreeTimeLimitedV2) - - requestID := uuid.NewV4() - validTo := time.Now().Add(time.Hour) - validFrom := time.Now().Local() - - // Insert a single signing order request. - err := suite.storage.InsertSigningOrderRequestOutbox(context.Background(), requestID, order.ID, - order.Items[0].ID, SigningOrderRequest{}) - suite.Require().NoError(err) - - // Create duplicate signed order results. - var kafkaDuplicates []SigningOrderResult - for i := 0; i < 10; i++ { - kafkaDuplicates = append(kafkaDuplicates, suite.makeMsg(requestID, order.ID, order.Items[0].ID, - issuer.ID, validTo, validFrom)) - } - - // Create Kafka messages from the duplicates signed order results. - codec, err := goavro.NewCodec(signingOrderResultSchema) - suite.Require().NoError(err) - - var kafkaMessages []kafka.Message - for i := 0; i < len(kafkaDuplicates); i++ { - b, err := json.Marshal(kafkaDuplicates[i]) - suite.Require().NoError(err) - - native, _, err := codec.NativeFromTextual(b) - suite.Require().NoError(err) - - binary, err := codec.BinaryFromNative(nil, native) - suite.Require().NoError(err) - - kafkaMessages = append(kafkaMessages, kafka.Message{ - Topic: kafkaUnsignedOrderCredsTopic, - Value: binary, - }) - } - - handler := SignedOrderCredentialsHandler{ - decoder: &SigningOrderResultDecoder{codec: codec}, - datastore: suite.storage, - } - - // Send them to handler with varied times and routines to mock different consumers. - var wg sync.WaitGroup - for i := 0; i < len(kafkaMessages); i++ { - wg.Add(1) - go func(index int) { - defer wg.Done() - time.Sleep(time.Millisecond * time.Duration(test.RandomIntWithMax(10))) - _ = handler.Handle(context.Background(), kafkaMessages[index]) - }(i) - } - wg.Wait() - - creds, err := suite.storage.GetTimeLimitedV2OrderCredsByOrder(order.ID) - suite.NoError(err) - - suite.Require().NotNil(creds) - suite.Assert().Len(creds.Credentials, 1) - suite.Assert().Equal(order.Items[0].ID, creds.Credentials[0].ItemID) -} - -func (suite *CredentialsTestSuite) TestSignedOrderCredentialsHandler_RequestDuplicates() { - // Create an issuer and a paid order with one order item and a time limited v2 credential type. - ctx := context.WithValue(context.Background(), appctx.WhitelistSKUsCTXKey, []string{devFreeTimeLimitedV2}) - order, issuer := createOrderAndIssuer(suite.T(), ctx, suite.storage, devFreeTimeLimitedV2) - - // Insert a ten signing order requests for the same order and item id - var requestIDs []uuid.UUID - for i := 0; i < 10; i++ { - requestIDs = append(requestIDs, uuid.NewV4()) - err := suite.storage.InsertSigningOrderRequestOutbox(context.Background(), requestIDs[i], order.ID, - order.Items[0].ID, SigningOrderRequest{}) - suite.Require().NoError(err) - } - - validTo := time.Now().Add(time.Hour) - validFrom := time.Now().Local() - - // Create signed order results for all the request with the same order, order item and valid to and valid from. - var signedOrderResults []SigningOrderResult - for i := 0; i < len(requestIDs); i++ { - signedOrderResults = append(signedOrderResults, suite.makeMsg(requestIDs[i], order.ID, order.Items[0].ID, - issuer.ID, validTo, validFrom)) - } - - // Create Kafka messages from the signed order results. - codec, err := goavro.NewCodec(signingOrderResultSchema) - suite.Require().NoError(err) - - var kafkaMessages []kafka.Message - for i := 0; i < len(signedOrderResults); i++ { - b, err := json.Marshal(signedOrderResults[i]) - suite.Require().NoError(err) - - native, _, err := codec.NativeFromTextual(b) - suite.Require().NoError(err) - - binary, err := codec.BinaryFromNative(nil, native) - suite.Require().NoError(err) - - kafkaMessages = append(kafkaMessages, kafka.Message{ - Topic: kafkaUnsignedOrderCredsTopic, - Value: binary, - }) - } - - handler := SignedOrderCredentialsHandler{ - decoder: &SigningOrderResultDecoder{codec: codec}, - datastore: suite.storage, - } - - // Send them to handler with varied times and routines to mock different consumers. - var wg sync.WaitGroup - for i := 0; i < len(kafkaMessages); i++ { - wg.Add(1) - go func(index int) { - defer wg.Done() - time.Sleep(time.Millisecond * time.Duration(test.RandomIntWithMax(100))) - _ = handler.Handle(context.Background(), kafkaMessages[index]) - }(i) - } - wg.Wait() - - creds, err := suite.storage.GetTimeLimitedV2OrderCredsByOrder(order.ID) - suite.NoError(err) - - suite.Require().NotNil(creds) - suite.Assert().Len(creds.Credentials, 1) - suite.Assert().Equal(order.Items[0].ID, creds.Credentials[0].ItemID) -} - -func TestCreateIssuer_NewIssuer(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - const merchantID = "brave.com" - - orderItem := &OrderItem{ - ID: uuid.NewV4(), - SKU: test.RandomString(), - ValidForISO: ptr.FromString("P1M"), - EachCredentialValidForISO: ptr.FromString("P1D"), - } - - issuerID, err := encodeIssuerID(merchantID, orderItem.SKU) - must.Equal(t, nil, err) - - cbrClient := mock_cbr.NewMockClient(ctrl) - cbrClient.EXPECT().CreateIssuer(ctx, issuerID, defaultMaxTokensPerIssuer).Return(nil) - - issuerResponse := &cbr.IssuerResponse{ - Name: issuerID, - PublicKey: test.RandomString(), - } - - cbrClient.EXPECT().GetIssuer(ctx, issuerID).Return(issuerResponse, nil) - - issuer := &Issuer{ - MerchantID: issuerResponse.Name, - PublicKey: issuerResponse.PublicKey, - } - - issuerRepo := &repository.MockIssuer{ - FnGetByMerchID: func(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - return nil, model.ErrIssuerNotFound - }, - - FnCreate: func(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - return issuer, nil - }, - } - - svc := &Service{ - issuerRepo: issuerRepo, - cbClient: cbrClient, - retry: backoff.Retry, - } - - { - err := svc.CreateIssuer(ctx, nil, merchantID, orderItem) - should.Equal(t, nil, err) - } -} - -func TestCreateIssuerV3_NewIssuer(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - const merchantID = "brave.com" - - orderItem := &OrderItem{ - ID: uuid.NewV4(), - SKU: test.RandomString(), - ValidForISO: ptr.FromString("P1M"), - EachCredentialValidForISO: ptr.FromString("P1D"), - } - - issuerID, err := encodeIssuerID(merchantID, orderItem.SKU) - must.Equal(t, nil, err) - - issuerConfig := model.IssuerConfig{ - Buffer: test.RandomInt(), - Overlap: test.RandomInt(), - } - - createIssuerV3 := cbr.IssuerRequest{ - Name: issuerID, - Cohort: defaultCohort, - MaxTokens: defaultMaxTokensPerIssuer, - ValidFrom: ptr.FromTime(time.Now()), - Duration: *orderItem.EachCredentialValidForISO, - Buffer: issuerConfig.Buffer, - Overlap: issuerConfig.Overlap, - } - - cbrClient := mock_cbr.NewMockClient(ctrl) - cbrClient.EXPECT().CreateIssuerV3(ctx, isCreateIssuerV3(createIssuerV3)).Return(nil) - - issuerResponse := &cbr.IssuerResponse{ - Name: issuerID, - PublicKey: test.RandomString(), - } - - cbrClient.EXPECT().GetIssuerV3(ctx, createIssuerV3.Name).Return(issuerResponse, nil) - - issuer := &Issuer{ - MerchantID: issuerResponse.Name, - PublicKey: issuerResponse.PublicKey, - } - - issuerRepo := &repository.MockIssuer{ - FnGetByMerchID: func(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - return nil, model.ErrIssuerNotFound - }, - - FnCreate: func(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - return issuer, nil - }, - } - - svc := &Service{ - issuerRepo: issuerRepo, - cbClient: cbrClient, - retry: backoff.Retry, - } - - { - err := svc.CreateIssuerV3(ctx, nil, merchantID, orderItem, issuerConfig) - should.Equal(t, nil, err) - } -} - -func TestCreateIssuer_AlreadyExists(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - const merchantID = "brave.com" - - orderItem := &OrderItem{ - ID: uuid.NewV4(), - SKU: test.RandomString(), - ValidForISO: ptr.FromString("P1M"), - EachCredentialValidForISO: ptr.FromString("P1D"), - } - - issuerID, err := encodeIssuerID(merchantID, orderItem.SKU) - must.Equal(t, nil, err) - - issuer := &Issuer{ - MerchantID: issuerID, - PublicKey: test.RandomString(), - } - - issuerRepo := &repository.MockIssuer{ - FnGetByMerchID: func(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - return issuer, nil - }, - - FnCreate: func(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - return nil, errors.New("unexpected") - }, - } - - svc := &Service{ - issuerRepo: issuerRepo, - } - - { - err := svc.CreateIssuer(ctx, nil, merchantID, orderItem) - should.Equal(t, nil, err) - } -} - -func TestCreateIssuerV3_AlreadyExists(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - const merchantID = "brave.com" - - orderItem := &OrderItem{ - ID: uuid.NewV4(), - SKU: test.RandomString(), - ValidForISO: ptr.FromString("P1M"), - EachCredentialValidForISO: ptr.FromString("P1D"), - } - - issuerID, err := encodeIssuerID(merchantID, orderItem.SKU) - must.Equal(t, nil, err) - - issuer := &Issuer{ - MerchantID: issuerID, - PublicKey: test.RandomString(), - } - - issuerRepo := &repository.MockIssuer{ - FnGetByMerchID: func(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - return issuer, nil - }, - - FnCreate: func(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - return nil, errors.New("unexpected") - }, - } - - svc := &Service{ - issuerRepo: issuerRepo, - } - - issuerConfig := model.IssuerConfig{ - Buffer: test.RandomInt(), - Overlap: test.RandomInt(), - } - - { - err := svc.CreateIssuerV3(ctx, nil, merchantID, orderItem, issuerConfig) - should.Equal(t, nil, err) - } -} - -func TestCanRetry(t *testing.T) { - t.Run("true", func(t *testing.T) { - err := clients.NewHTTPError( - errors.New(test.RandomString()), - test.RandomString(), - test.RandomString(), - http.StatusRequestTimeout, - nil, - ) - - fn := canRetry(dontRetryCodes) - should.Equal(t, true, fn(err)) - }) - - t.Run("false", func(t *testing.T) { - err := clients.NewHTTPError( - errors.New(test.RandomString()), - test.RandomString(), - test.RandomString(), - http.StatusForbidden, - nil, - ) - - fn := canRetry(dontRetryCodes) - should.Equal(t, false, fn(err)) - }) -} - -func TestCreateOrderCredentials(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - const merchantID = "brave.com" - - orderItem := &OrderItem{ - ID: uuid.NewV4(), - SKU: test.RandomString(), - ValidForISO: ptr.FromString("P1M"), - EachCredentialValidForISO: ptr.FromString("P1D"), - } - - issuerID, err := encodeIssuerID(merchantID, orderItem.SKU) - must.Equal(t, nil, err) - - issuer := &Issuer{ - MerchantID: issuerID, - PublicKey: test.RandomString(), - } - - issuerRepo := &repository.MockIssuer{ - FnGetByMerchID: func(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - return issuer, nil - }, - - FnCreate: func(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - return nil, errors.New("unexpected") - }, - } - - svc := &Service{ - issuerRepo: issuerRepo, - } - - issuerConfig := model.IssuerConfig{ - Buffer: test.RandomInt(), - Overlap: test.RandomInt(), - } - - { - err := svc.CreateIssuerV3(ctx, nil, merchantID, orderItem, issuerConfig) - should.Equal(t, nil, err) - } -} - -func TestDeduplicateCredentialBindings(t *testing.T) { - var tokens = []CredentialBinding{ - { - TokenPreimage: "totally_random", - }, - { - TokenPreimage: "totally_random_1", - }, - { - TokenPreimage: "totally_random", - }, - { - TokenPreimage: "totally_random_2", - }, - } - - var result = deduplicateCredentialBindings(tokens...) - if len(result) > len(tokens) { - t.Error("result should be less than number of tokens") - } - - var seen []CredentialBinding - for _, v := range result { - for _, vv := range seen { - if v == vv { - t.Error("Deduplication of tokens didn't work") - } - seen = append(seen, v) - } - } -} - -func TestIssuerID(t *testing.T) { - - cases := []struct { - MerchantID string - SKU string - }{ - { - MerchantID: "brave.com", - SKU: "anon-card-vote", - }, - { - MerchantID: "", - SKU: "anon-card-vote", - }, - { - MerchantID: "brave.com", - SKU: "", - }, - { - MerchantID: "", - SKU: "", - }, - } - - for _, v := range cases { - issuerID, err := encodeIssuerID(v.MerchantID, v.SKU) - if err != nil { - t.Error("failed to encode: ", err) - } - - merchantIDPrime, skuPrime, err := decodeIssuerID(issuerID) - if err != nil { - t.Error("failed to encode: ", err) - } - - if v.MerchantID != merchantIDPrime { - t.Errorf( - "merchantID does not match decoded: %s != %s", v.MerchantID, merchantIDPrime) - } - - if v.SKU != skuPrime { - t.Errorf( - "sku does not match decoded: %s != %s", v.SKU, skuPrime) - } - } -} - -func TestDecodeSignedOrderCredentials_Success(t *testing.T) { - codec, err := goavro.NewCodec(signingOrderResultSchema) - must.NoError(t, err) - - msg := &SigningOrderResult{ - RequestID: test.RandomString(), - Data: []SignedOrder{ - { - PublicKey: test.RandomString(), - Proof: test.RandomString(), - Status: SignedOrderStatusOk, - SignedTokens: []string{test.RandomString()}, - AssociatedData: []byte{}, - ValidFrom: &UnionNullString{"string": time.Now().Local().Format(time.RFC3339)}, - ValidTo: &UnionNullString{"string": time.Now().Add(1 * time.Hour).Local().Format(time.RFC3339)}, - BlindedTokens: []string{test.RandomString()}, - }, - }, - } - - textual, err := json.Marshal(msg) - must.NoError(t, err) - - native, _, err := codec.NativeFromTextual(textual) - must.NoError(t, err) - - binary, err := codec.BinaryFromNative(nil, native) - must.NoError(t, err) - - message := kafka.Message{ - Key: []byte(uuid.NewV4().String()), - Value: binary, - } - - d := SigningOrderResultDecoder{ - codec: codec, - } - - actual, err := d.Decode(message) - must.NoError(t, err) - - should.Equal(t, msg, actual) -} - -func (suite *CredentialsTestSuite) makeMsg(requestID, orderID, itemID, issuerID uuid.UUID, - to, from time.Time) SigningOrderResult { - - metadata := Metadata{ - ItemID: itemID, - OrderID: orderID, - IssuerID: issuerID, - CredentialType: timeLimitedV2, - } - - associatedData, err := json.Marshal(metadata) - suite.Require().NoError(err) - - return SigningOrderResult{ - RequestID: requestID.String(), - Data: []SignedOrder{ - { - PublicKey: test.RandomString(), - Proof: test.RandomString(), - Status: SignedOrderStatusOk, - BlindedTokens: []string{test.RandomString()}, - SignedTokens: []string{test.RandomString()}, - ValidTo: &UnionNullString{"string": to.Format(time.RFC3339)}, - ValidFrom: &UnionNullString{"string": from.Format(time.RFC3339)}, - AssociatedData: associatedData, - }, - }, - } -} - -func isCreateIssuerV3(expected cbr.IssuerRequest) gomock.Matcher { - return createIssuerV3Matcher{expected: expected} -} - -type createIssuerV3Matcher struct { - expected cbr.IssuerRequest -} - -func (c createIssuerV3Matcher) Matches(arg interface{}) bool { - actual := arg.(cbr.IssuerRequest) - return c.expected.Name == actual.Name && - c.expected.Cohort == actual.Cohort && - c.expected.MaxTokens == actual.MaxTokens && - c.expected.ValidFrom.Before(*actual.ValidFrom) && - c.expected.Duration == actual.Duration && - c.expected.Buffer == actual.Buffer && - c.expected.Overlap == actual.Overlap -} - -func (c createIssuerV3Matcher) String() string { - return "does not match" -} diff --git a/services/skus/datastore.go b/services/skus/datastore.go deleted file mode 100644 index 67937c96b..000000000 --- a/services/skus/datastore.go +++ /dev/null @@ -1,1442 +0,0 @@ -package skus - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "time" - - // needed for magic migration - _ "github.com/golang-migrate/migrate/v4/source/file" - - "github.com/getsentry/sentry-go" - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - "github.com/segmentio/kafka-go" - "github.com/shopspring/decimal" - - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/jsonutils" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/ptr" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -const ( - signingRequestBatchSize = 10 - - errNotFound = model.Error("not found") - errNoTLV2Creds = model.Error("no unexpired time-limited-v2 credentials found") -) - -// Datastore abstracts over the underlying datastore. -type Datastore interface { - datastore.Datastore - - CreateOrder(ctx context.Context, dbi sqlx.ExtContext, oreq *model.OrderNew, items []model.OrderItem) (*model.Order, error) - // SetOrderTrialDays - set the number of days of free trial for this order - SetOrderTrialDays(ctx context.Context, orderID *uuid.UUID, days int64) (*Order, error) - // GetOrder by ID - GetOrder(orderID uuid.UUID) (*Order, error) - // GetOrderByExternalID by the external id from the purchase vendor - GetOrderByExternalID(externalID string) (*Order, error) - // UpdateOrder updates an order when it has been paid - UpdateOrder(orderID uuid.UUID, status string) error - // UpdateOrderMetadata adds a key value pair to an order's metadata - UpdateOrderMetadata(orderID uuid.UUID, key string, value string) error - // CreateTransaction creates a transaction - CreateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (*Transaction, error) - // UpdateTransaction creates a transaction - UpdateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (*Transaction, error) - // GetTransaction returns a transaction given an external transaction id - GetTransaction(externalTransactionID string) (*Transaction, error) - // GetTransactions returns all the transactions for a specific order - GetTransactions(orderID uuid.UUID) (*[]Transaction, error) - // GetPagedMerchantTransactions returns all the transactions for a specific order - GetPagedMerchantTransactions(ctx context.Context, merchantID uuid.UUID, pagination *inputs.Pagination) (*[]Transaction, int, error) - // GetSumForTransactions gets a decimal sum of for transactions for an order - GetSumForTransactions(orderID uuid.UUID) (decimal.Decimal, error) - - GetIssuerByPublicKey(publicKey string) (*Issuer, error) - DeleteSingleUseOrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID, isSigned bool) error - // GetOrderCredsByItemID retrieves an order credential by item id - GetOrderCredsByItemID(orderID uuid.UUID, itemID uuid.UUID, isSigned bool) (*OrderCreds, error) - GetKeysByMerchant(merchant string, showExpired bool) (*[]Key, error) - GetKey(id uuid.UUID, showExpired bool) (*Key, error) - CreateKey(merchant string, name string, encryptedSecretKey string, nonce string) (*Key, error) - DeleteKey(id uuid.UUID, delaySeconds int) (*Key, error) - GetUncommittedVotesForUpdate(ctx context.Context) (*sqlx.Tx, []*VoteRecord, error) - CommitVote(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) error - MarkVoteErrored(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) error - InsertVote(ctx context.Context, vr VoteRecord) error - CheckExpiredCheckoutSession(uuid.UUID) (bool, string, error) - IsStripeSub(uuid.UUID) (bool, string, error) - GetOrderItem(ctx context.Context, itemID uuid.UUID) (*OrderItem, error) - InsertOrderCredsTx(ctx context.Context, tx *sqlx.Tx, creds *OrderCreds) error - GetOrderCreds(orderID uuid.UUID, isSigned bool) ([]OrderCreds, error) - SendSigningRequest(ctx context.Context, signingRequestWriter SigningRequestWriter) error - InsertSignedOrderCredentialsTx(ctx context.Context, tx *sqlx.Tx, signedOrderResult *SigningOrderResult) error - AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (bool, error) - GetTimeLimitedV2OrderCredsByOrder(orderID uuid.UUID) (*TimeLimitedV2Creds, error) - GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID uuid.UUID) (*TimeLimitedV2Creds, error) - DeleteTimeLimitedV2OrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) error - GetTimeLimitedV2OrderCredsByOrderItem(itemID uuid.UUID) (*TimeLimitedV2Creds, error) - InsertTimeLimitedV2OrderCredsTx(ctx context.Context, tx *sqlx.Tx, tlv2 TimeAwareSubIssuedCreds) error - InsertSigningOrderRequestOutbox(ctx context.Context, requestID uuid.UUID, orderID uuid.UUID, itemID uuid.UUID, signingOrderRequest SigningOrderRequest) error - GetSigningOrderRequestOutboxByRequestID(ctx context.Context, dbi sqlx.QueryerContext, reqID uuid.UUID) (*SigningOrderRequestOutbox, error) - GetSigningOrderRequestOutboxByOrder(ctx context.Context, orderID uuid.UUID) ([]SigningOrderRequestOutbox, error) - GetSigningOrderRequestOutboxByOrderItem(ctx context.Context, itemID uuid.UUID) ([]SigningOrderRequestOutbox, error) - DeleteSigningOrderRequestOutboxByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) error - UpdateSigningOrderRequestOutboxTx(ctx context.Context, tx *sqlx.Tx, requestID uuid.UUID, completedAt time.Time) error - SetOrderPaid(context.Context, *uuid.UUID) error - AppendOrderMetadata(context.Context, *uuid.UUID, string, string) error - AppendOrderMetadataInt(context.Context, *uuid.UUID, string, int) error - AppendOrderMetadataInt64(context.Context, *uuid.UUID, string, int64) error - GetOutboxMovAvgDurationSeconds() (int64, error) - ExternalIDExists(context.Context, string) (bool, error) -} - -type orderStore interface { - Get(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) - GetByExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (*model.Order, error) - Create(ctx context.Context, dbi sqlx.QueryerContext, oreq *model.OrderNew) (*model.Order, error) - SetLastPaidAt(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error - SetTrialDays(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID, ndays int64) (*model.Order, error) - SetStatus(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, status string) error - GetExpiresAtAfterISOPeriod(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (time.Time, error) - SetExpiresAt(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error - UpdateMetadata(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, data datastore.Metadata) error - AppendMetadata(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, key, val string) error - AppendMetadataInt(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, key string, val int) error - AppendMetadataInt64(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, key string, val int64) error - GetExpiredStripeCheckoutSessionID(ctx context.Context, dbi sqlx.QueryerContext, orderID uuid.UUID) (string, error) - HasExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (bool, error) - GetMetadata(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (datastore.Metadata, error) -} - -type orderItemStore interface { - Get(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.OrderItem, error) - FindByOrderID(ctx context.Context, dbi sqlx.QueryerContext, orderID uuid.UUID) ([]model.OrderItem, error) - InsertMany(ctx context.Context, dbi sqlx.ExtContext, items ...model.OrderItem) ([]model.OrderItem, error) -} - -type orderPayHistoryStore interface { - Insert(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error -} - -type issuerStore interface { - GetByMerchID(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) - GetByPubKey(ctx context.Context, dbi sqlx.QueryerContext, pubKey string) (*model.Issuer, error) - Create(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) -} - -// VoteRecord - how the ac votes are stored in the queue -type VoteRecord struct { - ID uuid.UUID - RequestCredentials string - VoteText string - VoteEventBinary []byte - Erred bool - Processed bool -} - -// Postgres is a Datastore wrapper around a postgres database -type Postgres struct { - datastore.Postgres - - orderRepo orderStore - orderItemRepo orderItemStore - orderPayHistory orderPayHistoryStore - issuerRepo issuerStore -} - -// NewPostgres creates a new Postgres Datastore. -func NewPostgres( - orderRepo orderStore, - orderItemRepo orderItemStore, - orderPayHistory orderPayHistoryStore, - issuerRepo issuerStore, - databaseURL string, - performMigration bool, - migrationTrack string, - dbStatsPrefix ...string, -) (Datastore, error) { - pg, err := newPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if err != nil { - return nil, err - } - - pg.orderRepo = orderRepo - pg.orderItemRepo = orderItemRepo - pg.orderPayHistory = orderPayHistory - pg.issuerRepo = issuerRepo - - return &DatastoreWithPrometheus{base: pg, instanceName: "payment_datastore"}, nil -} - -func newPostgres(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (*Postgres, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if err != nil { - return nil, err - } - - return &Postgres{Postgres: *pg}, nil -} - -// CreateKey creates an encrypted key in the database based on the merchant -func (pg *Postgres) CreateKey(merchant string, name string, encryptedSecretKey string, nonce string) (*Key, error) { - // interface and create an api key - var key Key - err := pg.RawDB().Get(&key, ` - INSERT INTO api_keys (merchant_id, name, encrypted_secret_key, nonce) - VALUES ($1, $2, $3, $4) - RETURNING id, name, merchant_id, encrypted_secret_key, nonce, created_at, expiry - `, - merchant, name, encryptedSecretKey, nonce) - - if err != nil { - return nil, fmt.Errorf("failed to create key for merchant: %w", err) - } - return &key, nil -} - -// DeleteKey updates a key with an expiration time based on the id -func (pg *Postgres) DeleteKey(id uuid.UUID, delaySeconds int) (*Key, error) { - var key Key - err := pg.RawDB().Get(&key, ` - UPDATE api_keys - SET expiry=(current_timestamp + $2) - WHERE id=$1 - RETURNING id, name, merchant_id, created_at, expiry - `, id.String(), fmt.Sprintf("%vs", delaySeconds)) - - if err == sql.ErrNoRows { - return nil, nil - } else if err != nil { - return nil, fmt.Errorf("failed to update key for merchant: %w", err) - } - - return &key, nil -} - -// GetKeysByMerchant returns a list of active API keys -func (pg *Postgres) GetKeysByMerchant(merchant string, showExpired bool) (*[]Key, error) { - expiredQuery := "AND (expiry IS NULL or expiry > CURRENT_TIMESTAMP)" - if showExpired { - expiredQuery = "" - } - - var keys []Key - err := pg.RawDB().Select(&keys, ` - select - id, name, merchant_id, created_at, expiry, - encrypted_secret_key, nonce - from api_keys - where - merchant_id = $1`+expiredQuery+" ORDER BY name, created_at", - merchant) - - if err != nil { - return nil, fmt.Errorf("failed to get keys for merchant: %w", err) - } - - return &keys, nil -} - -// GetKey returns the specified key, conditionally checking if it is expired -func (pg *Postgres) GetKey(id uuid.UUID, showExpired bool) (*Key, error) { - expiredQuery := " AND (expiry IS NULL or expiry > CURRENT_TIMESTAMP)" - if showExpired { - expiredQuery = "" - } - - var key Key - err := pg.RawDB().Get(&key, ` - select - id, name, merchant_id, created_at, expiry, - encrypted_secret_key, nonce - from api_keys - where - id = $1`+expiredQuery, - id.String()) - - if err != nil { - return nil, fmt.Errorf("failed to get key: %w", err) - } - - return &key, nil -} - -// SetOrderTrialDays sets the number of days of free trial for this order and returns the updated result. -func (pg *Postgres) SetOrderTrialDays(ctx context.Context, orderID *uuid.UUID, days int64) (*Order, error) { - tx, err := pg.RawDB().BeginTxx(ctx, nil) - if err != nil { - return nil, fmt.Errorf("failed to create db tx: %w", err) - } - defer pg.RollbackTx(tx) - - result, err := pg.orderRepo.SetTrialDays(ctx, tx, *orderID, days) - if err != nil { - return nil, fmt.Errorf("failed to execute tx: %w", err) - } - - result.Items, err = pg.orderItemRepo.FindByOrderID(ctx, tx, *orderID) - if err != nil { - return nil, err - } - - if err := tx.Commit(); err != nil { - return nil, err - } - - return result, nil -} - -// CreateOrder creates an order from the given prototype, and inserts items. -func (pg *Postgres) CreateOrder(ctx context.Context, dbi sqlx.ExtContext, oreq *model.OrderNew, items []model.OrderItem) (*model.Order, error) { - result, err := pg.orderRepo.Create(ctx, dbi, oreq) - if err != nil { - return nil, err - } - - if oreq.Status == OrderStatusPaid { - if err := pg.recordOrderPayment(ctx, dbi, result.ID, time.Now()); err != nil { - return nil, fmt.Errorf("failed to record order payment: %w", err) - } - } - - model.OrderItemList(items).SetOrderID(result.ID) - - result.Items, err = pg.orderItemRepo.InsertMany(ctx, dbi, items...) - if err != nil { - return nil, err - } - - return result, nil -} - -// GetOrderByExternalID returns an order by the external id from the purchase vendor. -func (pg *Postgres) GetOrderByExternalID(externalID string) (*Order, error) { - ctx := context.TODO() - dbi := pg.RawDB() - - result, err := pg.orderRepo.GetByExternalID(ctx, dbi, externalID) - if err != nil { - // Preserve the legacy behaviour. - // TODO: Propagate the sentinel error, and handle in the business logic properly. - if errors.Is(err, model.ErrOrderNotFound) { - return nil, nil - } - - return nil, err - } - - result.Items, err = pg.orderItemRepo.FindByOrderID(ctx, dbi, result.ID) - if err != nil { - return nil, err - } - - return result, nil -} - -// GetOutboxMovAvgDurationSeconds - get the number of seconds it takes to clear the last 20 outbox messages -func (pg *Postgres) GetOutboxMovAvgDurationSeconds() (int64, error) { - statement := ` - select - coalesce(ceiling(extract(epoch from avg(completed_at-created_at))),1) - from - (select completed_at, created_at from signing_order_request_outbox order by completed_at desc limit 20) as q; -` - var seconds int64 - if err := pg.RawDB().Get(&seconds, statement); err != nil { - return 0, err - } - if seconds > 5 { - // set max allowable retry after - seconds = 5 - } - return seconds, nil -} - -// GetOrder returns an order from the database. -func (pg *Postgres) GetOrder(orderID uuid.UUID) (*Order, error) { - ctx := context.TODO() - dbi := pg.RawDB() - - result, err := pg.orderRepo.Get(ctx, dbi, orderID) - if err != nil { - // Preserve the legacy behaviour. - // TODO: Propagate the sentinel error, and handle in the business logic properly. - if errors.Is(err, model.ErrOrderNotFound) { - return nil, nil - } - - return nil, err - } - - result.Items, err = pg.orderItemRepo.FindByOrderID(ctx, dbi, orderID) - if err != nil { - return nil, err - } - - return result, nil -} - -// GetOrderItem retrieves the order item for the given identifier. -// -// It returns sql.ErrNoRows if the item is not found. -func (pg *Postgres) GetOrderItem(ctx context.Context, itemID uuid.UUID) (*OrderItem, error) { - result, err := pg.orderItemRepo.Get(ctx, pg.RawDB(), itemID) - if err != nil { - // Preserve the legacy behaviour. - // TODO: Propagate the sentinel error, and handle in the business logic properly. - if errors.Is(err, model.ErrOrderItemNotFound) { - return nil, sql.ErrNoRows - } - - return nil, err - } - - return result, nil -} - -// GetPagedMerchantTransactions - get a paginated list of transactions for a merchant -func (pg *Postgres) GetPagedMerchantTransactions( - ctx context.Context, merchantID uuid.UUID, pagination *inputs.Pagination) (*[]Transaction, int, error) { - var ( - total int - err error - ) - - countStatement := ` - SELECT count(t.*) as total - FROM transactions as t - INNER JOIN orders as o ON o.id = t.order_id - WHERE o.merchant_id = $1` - - // get the total count - row := pg.RawDB().QueryRow(countStatement, merchantID) - - if err := row.Scan(&total); err != nil { - return nil, 0, err - } - - getStatement := ` - SELECT t.* - FROM transactions as t - INNER JOIN orders as o ON o.id = t.order_id - WHERE o.merchant_id = $1 - ` - - // $ numbered params for query - params := []interface{}{ - merchantID, - } - - orderBy := pagination.GetOrderBy(ctx) - if orderBy != "" { - getStatement += fmt.Sprintf(" ORDER BY %s", orderBy) - } - - offset := pagination.Page * pagination.Items - if offset > 0 { - getStatement += fmt.Sprintf(" OFFSET %d", offset) - } - - if pagination.Items > 0 { - getStatement += fmt.Sprintf(" FETCH NEXT %d ROWS ONLY", pagination.Items) - } - - transactions := []Transaction{} - - rows, err := pg.RawDB().Queryx(getStatement, params...) - if err != nil { - return nil, 0, err - } - for rows.Next() { - var transaction = new(Transaction) - err := rows.StructScan(transaction) - if err != nil { - return nil, 0, err - } - transactions = append(transactions, *transaction) - } - err = rows.Close() - if err != nil { - return nil, 0, err - } - - return &transactions, total, nil -} - -// GetTransactions returns the list of transactions given an orderID -func (pg *Postgres) GetTransactions(orderID uuid.UUID) (*[]Transaction, error) { - statement := ` - SELECT id, order_id, created_at, updated_at, external_transaction_id, status, currency, kind, amount - FROM transactions WHERE order_id = $1` - transactions := []Transaction{} - err := pg.RawDB().Select(&transactions, statement, orderID) - - if err != nil { - return nil, err - } - - return &transactions, nil -} - -// GetTransaction returns a single of transaction given an external transaction Id -func (pg *Postgres) GetTransaction(externalTransactionID string) (*Transaction, error) { - statement := ` - SELECT id, order_id, created_at, updated_at, external_transaction_id, status, currency, kind, amount - FROM transactions WHERE external_transaction_id = $1` - transaction := Transaction{} - err := pg.RawDB().Get(&transaction, statement, externalTransactionID) - - if err == sql.ErrNoRows { - return nil, nil - } else if err != nil { - return nil, err - } - - return &transaction, nil -} - -// CheckExpiredCheckoutSession indicates whether a Stripe checkout session is expired with its id for the given orderID. -// -// TODO(pavelb): The boolean return value is unnecessary, and can be removed. -// If there is experied session, the session id is present. -// If there is no session, or it has not expired, the result is the same – no session id. -// It's the caller's responsibility (the business logic layer) to interpret the result. -func (pg *Postgres) CheckExpiredCheckoutSession(orderID uuid.UUID) (bool, string, error) { - ctx := context.TODO() - - sessID, err := pg.orderRepo.GetExpiredStripeCheckoutSessionID(ctx, pg.RawDB(), orderID) - if err != nil { - if errors.Is(err, model.ErrExpiredStripeCheckoutSessionIDNotFound) { - return false, "", nil - } - - return false, "", fmt.Errorf("failed to check expired state of checkout session: %w", err) - } - - if sessID == "" { - return false, "", nil - } - - return true, sessID, nil -} - -func (pg *Postgres) ExternalIDExists(ctx context.Context, externalID string) (bool, error) { - return pg.orderRepo.HasExternalID(ctx, pg.RawDB(), externalID) -} - -// IsStripeSub reports whether the order is associated with a stripe subscription, if true, subscription id is returned. -// -// TODO(pavelb): This is a piece of business logic that leaked to the storage layer. -// Also, it unsuccessfully mimics the Go comma, ok idiom – bool and string should be swapped. -// But that's not necessary. -// If metadata was found, but there was no stripeSubscriptionId, it's known not to be a Stripe order. -func (pg *Postgres) IsStripeSub(orderID uuid.UUID) (bool, string, error) { - ctx := context.TODO() - - data, err := pg.orderRepo.GetMetadata(ctx, pg.RawDB(), orderID) - if err != nil { - return false, "", err - } - - sid, ok := data["stripeSubscriptionId"].(string) - - return ok, sid, nil -} - -// UpdateOrder updates the orders status. -// -// Status should either be one of pending, paid, fulfilled, or canceled. -// -// TODO: rename it to better reflect the behaviour. -func (pg *Postgres) UpdateOrder(orderID uuid.UUID, status string) error { - ctx := context.Background() - - tx, err := pg.RawDB().BeginTxx(ctx, nil) - if err != nil { - return err - } - defer pg.RollbackTx(tx) - - if err := pg.orderRepo.SetStatus(ctx, tx, orderID, status); err != nil { - return err - } - - if status == OrderStatusPaid { - if err := pg.recordOrderPayment(ctx, tx, orderID, time.Now()); err != nil { - return fmt.Errorf("failed to record order payment: %w", err) - } - - if err := pg.updateOrderExpiresAt(ctx, tx, orderID); err != nil { - return fmt.Errorf("failed to set order expires_at: %w", err) - } - } - - return tx.Commit() -} - -// CreateTransaction creates a transaction given an orderID, externalTransactionID, currency, and a kind of transaction -func (pg *Postgres) CreateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (*Transaction, error) { - tx := pg.RawDB().MustBegin() - defer pg.RollbackTx(tx) - - var transaction Transaction - err := tx.Get(&transaction, - ` - INSERT INTO transactions (order_id, external_transaction_id, status, currency, kind, amount) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id, order_id, created_at, updated_at, external_transaction_id, status, currency, kind, amount - `, orderID, externalTransactionID, status, currency, kind, amount) - - if err != nil { - return nil, err - } - - err = tx.Commit() - - if err != nil { - return nil, err - } - - return &transaction, nil -} - -// UpdateTransaction creates a transaction given an orderID, externalTransactionID, currency, and a kind of transaction -func (pg *Postgres) UpdateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (*Transaction, error) { - tx := pg.RawDB().MustBegin() - defer pg.RollbackTx(tx) - - var transaction Transaction - err := tx.Get(&transaction, - ` - update transactions set status=$1, currency=$2, kind=$3, amount=$4 - where external_transaction_id=$5 and order_id=$6 - RETURNING id, order_id, created_at, updated_at, external_transaction_id, status, currency, kind, amount - `, status, currency, kind, amount, externalTransactionID, orderID) - - if err != nil { - return nil, err - } - - err = tx.Commit() - - if err != nil { - return nil, err - } - - return &transaction, nil -} - -// GetSumForTransactions returns the calculated sum -func (pg *Postgres) GetSumForTransactions(orderID uuid.UUID) (decimal.Decimal, error) { - var sum decimal.Decimal - - err := pg.RawDB().Get(&sum, ` - SELECT COALESCE(SUM(amount), 0.0) as sum - FROM transactions - WHERE order_id = $1 AND status = 'completed' - `, orderID) - - return sum, err -} - -// GetIssuerByPublicKey returns an issuer by the pubKey. -// -// Deprecated: Use the corresponding repository directly with GetByPubKey. -func (pg *Postgres) GetIssuerByPublicKey(pubKey string) (*Issuer, error) { - result, err := pg.issuerRepo.GetByPubKey(context.TODO(), pg.RawDB(), pubKey) - if err != nil { - // Preserve the old behaviour. - // TODO: Fix this as it defeats the purpose of multiple returns and has risks for callers. - // Thankfully, there is only one caller, but that coller, hypothetically, might panic. - if errors.Is(err, model.ErrIssuerNotFound) { - return nil, nil - } - - return nil, err - } - - return result, nil -} - -// InsertOrderCredsTx inserts the given order creds. -func (pg *Postgres) InsertOrderCredsTx(ctx context.Context, tx *sqlx.Tx, creds *OrderCreds) error { - blindedCredsJSON, err := json.Marshal(creds.BlindedCreds) - if err != nil { - return err - } - - statement := ` - insert into order_creds (item_id, order_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key) - values ($1, $2, $3, $4, $5, $6, $7)` - _, err = tx.ExecContext(ctx, statement, creds.ID, creds.OrderID, creds.IssuerID, blindedCredsJSON, - creds.SignedCreds, creds.BatchProof, creds.PublicKey) - - return err -} - -// GetOrderCreds returns the order credentials for a OrderID -func (pg *Postgres) GetOrderCreds(orderID uuid.UUID, isSigned bool) ([]OrderCreds, error) { - query := ` - select item_id, order_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key - from order_creds - where order_id = $1` - if isSigned { - query += " and signed_creds is not null" - } - - var orderCreds []OrderCreds - err := pg.RawDB().Select(&orderCreds, query, orderID) - if err != nil { - return nil, err - } - - if len(orderCreds) > 0 { - return orderCreds, nil - } - - return nil, nil -} - -// GetOrderTimeLimitedV2Creds returns all order credentials for an order. -func (pg *Postgres) GetOrderTimeLimitedV2Creds(orderID uuid.UUID) (*[]TimeLimitedV2Creds, error) { - // each "order item" is a different record - var timeLimitedV2Creds []TimeLimitedV2Creds - var timeAwareSubIssuedCreds []TimeAwareSubIssuedCreds - - // get all the credentials related to the order_id - query := ` - select order_id, item_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key, valid_from, valid_to - from order_creds - where order_id = $1 - - ` - - err := pg.RawDB().Select(&timeAwareSubIssuedCreds, query, orderID) - if err != nil { - return nil, err - } - - // convert our time aware creds into result - itemCredMap := map[uuid.UUID][]TimeAwareSubIssuedCreds{} - for i := range timeAwareSubIssuedCreds { - itemCredMap[timeAwareSubIssuedCreds[i].ItemID] = append(itemCredMap[timeAwareSubIssuedCreds[i].ItemID], timeAwareSubIssuedCreds[i]) - } - - for _, v := range itemCredMap { - if len(v) > 0 { - timeLimitedV2Creds = append(timeLimitedV2Creds, TimeLimitedV2Creds{ - OrderID: v[0].OrderID, - IssuerID: v[0].IssuerID, - Credentials: v, - }) - } - } - - if len(timeLimitedV2Creds) > 0 { - return &timeLimitedV2Creds, nil - } - - return nil, nil -} - -// GetOrderTimeLimitedV2CredsByItemID returns the order credentials by order and item. -func (pg *Postgres) GetOrderTimeLimitedV2CredsByItemID(orderID uuid.UUID, itemID uuid.UUID) (*TimeLimitedV2Creds, error) { - query := ` - select - order_id, item_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key, valid_from, valid_to - from order_creds - where - order_id = $1 - and item_id = $2 - and valid_to is not null - and valid_from is not null - ` - - var timeAwareCreds []TimeAwareSubIssuedCreds - err := pg.RawDB().Select(&timeAwareCreds, query, orderID, itemID) - if err != nil { - return nil, fmt.Errorf("error getting time aware creds: %w", err) - } - - if len(timeAwareCreds) == 0 { - return nil, nil - } - - return &TimeLimitedV2Creds{ - OrderID: timeAwareCreds[0].OrderID, - IssuerID: timeAwareCreds[0].IssuerID, - Credentials: timeAwareCreds, - }, nil -} - -// DeleteSingleUseOrderCredsByOrderTx performs a hard delete all single use order credentials for a given OrderID. -func (pg *Postgres) DeleteSingleUseOrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID, isSigned bool) error { - query := `delete from order_creds where order_id = $1` - if isSigned { - query += " and signed_creds is not null" - } - - _, err := tx.ExecContext(ctx, query, orderID) - if err != nil { - return fmt.Errorf("error deleting single use order creds: %w", err) - } - - return nil -} - -// DeleteTimeLimitedV2OrderCredsByOrderTx performs a hard delete for all time limited v2 order -// credentials for a given OrderID. -func (pg *Postgres) DeleteTimeLimitedV2OrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) error { - _, err := tx.ExecContext(ctx, `delete from time_limited_v2_order_creds where order_id = $1`, orderID) - if err != nil { - return fmt.Errorf("error deleting time limited v2 order creds: %w", err) - } - return nil -} - -// GetOrderCredsByItemID returns the order credentials for a OrderID by the itemID. -func (pg *Postgres) GetOrderCredsByItemID(orderID uuid.UUID, itemID uuid.UUID, isSigned bool) (*OrderCreds, error) { - orderCreds := OrderCreds{} - - query := ` - SELECT item_id, order_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key - FROM order_creds - WHERE order_id = $1 AND item_id = $2` - if isSigned { - query += " and signed_creds is not null" - } - - err := pg.RawDB().Get(&orderCreds, query, orderID, itemID) - if err == sql.ErrNoRows { - return nil, nil - } else if err != nil { - return nil, err - } - - return &orderCreds, nil -} - -// GetUncommittedVotesForUpdate - row locking on number of votes we will be pulling -// returns a transaction to commit, the vote records, and an error -func (pg *Postgres) GetUncommittedVotesForUpdate(ctx context.Context) (*sqlx.Tx, []*VoteRecord, error) { - var ( - results = make([]*VoteRecord, 100) - tx, err = pg.RawDB().Beginx() - ) - - if err != nil { - return tx, nil, fmt.Errorf("failed to aquire transaction: %w", err) - } - - statement := ` -select - id, credentials, vote_text, vote_event, erred, processed -from - vote_drain -where - processed = false AND - erred = false -limit 100 -FOR UPDATE -` - rows, err := tx.QueryContext(ctx, statement) - if err != nil { - return tx, nil, fmt.Errorf("failed to perform query for vote drain: %w", err) - } - - for rows.Next() { - var vr = new(VoteRecord) - if err := rows.Scan(&vr.ID, &vr.RequestCredentials, &vr.VoteText, - &vr.VoteEventBinary, &vr.Erred, &vr.Processed); err != nil { - return tx, nil, fmt.Errorf("failed to scan vote drain record: %w", err) - } - // add to results - results = append(results, vr) - } - if err := rows.Err(); err != nil { - return tx, nil, fmt.Errorf("row errors after scanning vote drain: %w", err) - } - - if err := rows.Close(); err != nil { - return tx, results, fmt.Errorf("error closing rows: %w", err) - } - - return tx, results, err -} - -// MarkVoteErrored - Update a vote to show it has errored, designed to run on a transaction so -// a batch number of votes can be processed. -func (pg *Postgres) MarkVoteErrored(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) error { - logger := logging.Logger(ctx, "skus.MarkVoteErrored") - logger.Debug().Msg("about to set errored to true for this vote") - - var statement = `update vote_drain set erred=true where id=$1` - _, err := tx.ExecContext(ctx, statement, vr.ID) - - if err != nil { - logger.Error().Err(err).Msg("failed to update vote_drain") - return fmt.Errorf("failed to commit vote from drain: %w", err) - } - return nil -} - -// CommitVote - Update a vote to show it has been processed, designed to run on a transaction so -// a batch number of votes can be processed. -func (pg *Postgres) CommitVote(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) error { - logger := logging.Logger(ctx, "skus.CommitVote") - logger.Debug().Msg("about to set processed to true for this vote") - - var statement = `update vote_drain set processed=true where id=$1` - _, err := tx.ExecContext(ctx, statement, vr.ID) - - if err != nil { - logger.Error().Err(err).Msg("unable to update processed=true for vote drain job") - return fmt.Errorf("failed to commit vote from drain: %w", err) - } - return nil -} - -// InsertVote - Add a vote to our "queue" to be processed -func (pg *Postgres) InsertVote(ctx context.Context, vr VoteRecord) error { - var ( - statement = ` - insert into vote_drain (credentials, vote_text, vote_event) - values ($1, $2, $3)` - _, err = pg.RawDB().ExecContext(ctx, statement, vr.RequestCredentials, vr.VoteText, vr.VoteEventBinary) - ) - if err != nil { - return fmt.Errorf("failed to insert vote to drain: %w", err) - } - return nil -} - -// UpdateOrderMetadata sets the order's metadata to the key and value. -// -// Deprecated: This method is no longer used and should be deleted. -// -// TODO(pavelb): Remove this method as it's dangerous and must not be used. -func (pg *Postgres) UpdateOrderMetadata(orderID uuid.UUID, key string, value string) error { - return model.Error("UpdateOrderMetadata must not be used") -} - -// TimeLimitedV2Creds represent all the -type TimeLimitedV2Creds struct { - OrderID uuid.UUID `json:"orderId"` - IssuerID uuid.UUID `json:"issuerId" ` - Credentials []TimeAwareSubIssuedCreds `json:"credentials"` -} - -// TimeAwareSubIssuedCreds sub issued time aware credentials -type TimeAwareSubIssuedCreds struct { - OrderID uuid.UUID `json:"orderId" db:"order_id"` - ItemID uuid.UUID `json:"itemId" db:"item_id"` - IssuerID uuid.UUID `json:"issuerId" db:"issuer_id"` - ValidTo time.Time `json:"validTo" db:"valid_to"` - ValidFrom time.Time `json:"validFrom" db:"valid_from"` - BlindedCreds jsonutils.JSONStringArray `json:"blindedCreds" db:"blinded_creds"` - SignedCreds jsonutils.JSONStringArray `json:"signedCreds" db:"signed_creds"` - BatchProof string `json:"batchProof" db:"batch_proof"` - PublicKey string `json:"publicKey" db:"public_key"` - RequestID string `json:"-" db:"request_id"` -} - -func (pg *Postgres) AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (bool, error) { - if len(blindedCreds) < 1 { - return false, errors.New("invalid parameter to tlv2 creds signed") - } - - query := ` - select exists( - select 1 from time_limited_v2_order_creds where blinded_creds->>0 = $1 - ) - ` - - var alreadySubmitted bool - err := pg.RawDB().Get(&alreadySubmitted, query, blindedCreds[0]) - if err != nil { - return false, err - } - - return alreadySubmitted, nil -} - -// GetTimeLimitedV2OrderCredsByOrder returns all the non expired time limited v2 order credentials for a given order. -func (pg *Postgres) GetTimeLimitedV2OrderCredsByOrder(orderID uuid.UUID) (*TimeLimitedV2Creds, error) { - query := ` - select order_id, item_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key, - valid_from, valid_to - from time_limited_v2_order_creds - where order_id = $1 and valid_to > now() - ` - - var timeAwareSubIssuedCreds []TimeAwareSubIssuedCreds - err := pg.RawDB().Select(&timeAwareSubIssuedCreds, query, orderID) - if err != nil { - return nil, err - } - - if len(timeAwareSubIssuedCreds) == 0 { - return nil, nil - } - - timeLimitedV2Creds := TimeLimitedV2Creds{ - OrderID: timeAwareSubIssuedCreds[0].OrderID, - IssuerID: timeAwareSubIssuedCreds[0].IssuerID, - Credentials: timeAwareSubIssuedCreds, - } - - return &timeLimitedV2Creds, nil -} - -// GetTLV2Creds returns all the non expired tlv2 credentials for a given order, item and request ids. -// -// If no credentials have been found, the method returns errNoTLV2Creds. -func (pg *Postgres) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID uuid.UUID) (*TimeLimitedV2Creds, error) { - const q = `SELECT - order_id, item_id, issuer_id, blinded_creds, signed_creds, - batch_proof, public_key, valid_from, valid_to - FROM time_limited_v2_order_creds - WHERE order_id = $1 AND item_id = $2 AND request_id = $3 AND valid_to > now()` - - creds := make([]TimeAwareSubIssuedCreds, 0) - if err := sqlx.SelectContext(ctx, dbi, &creds, q, ordID, itemID, reqID); err != nil { - return nil, err - } - - if len(creds) == 0 { - return nil, errNoTLV2Creds - } - - result := &TimeLimitedV2Creds{ - OrderID: creds[0].OrderID, - IssuerID: creds[0].IssuerID, - Credentials: creds, - } - - return result, nil -} - -// GetTimeLimitedV2OrderCredsByOrderItem returns all the order credentials for a single order item. -func (pg *Postgres) GetTimeLimitedV2OrderCredsByOrderItem(itemID uuid.UUID) (*TimeLimitedV2Creds, error) { - query := ` - select order_id, item_id, issuer_id, blinded_creds, signed_creds, batch_proof, public_key, - valid_from, valid_to - from time_limited_v2_order_creds - where item_id = $1 and valid_to > now() - ` - - var timeAwareSubIssuedCreds []TimeAwareSubIssuedCreds - err := pg.RawDB().Select(&timeAwareSubIssuedCreds, query, itemID) - if err != nil { - return nil, fmt.Errorf("error getting time aware creds: %w", err) - } - - if len(timeAwareSubIssuedCreds) == 0 { - return nil, nil - } - - return &TimeLimitedV2Creds{ - OrderID: timeAwareSubIssuedCreds[0].OrderID, - IssuerID: timeAwareSubIssuedCreds[0].IssuerID, - Credentials: timeAwareSubIssuedCreds, - }, nil -} - -// InsertTimeLimitedV2OrderCredsTx inserts time limited v2 credentials. -func (pg *Postgres) InsertTimeLimitedV2OrderCredsTx(ctx context.Context, tx *sqlx.Tx, tlv2 TimeAwareSubIssuedCreds) error { - blindedCredsJSON, err := json.Marshal(tlv2.BlindedCreds) - if err != nil { - return fmt.Errorf("error marshaling blinded creds: %w", err) - } - - signedCredsJSON, err := json.Marshal(tlv2.SignedCreds) - if err != nil { - return fmt.Errorf("error marshaling signed creds: %w", err) - } - - // continue to insert the credential - query := `insert into time_limited_v2_order_creds(item_id, order_id, issuer_id, blinded_creds, - signed_creds, batch_proof, public_key, valid_to, valid_from, request_id) - values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) on conflict do nothing` - - _, err = tx.ExecContext(ctx, query, tlv2.ItemID, tlv2.OrderID, tlv2.IssuerID, blindedCredsJSON, - signedCredsJSON, tlv2.BatchProof, tlv2.PublicKey, tlv2.ValidTo, tlv2.ValidFrom, tlv2.RequestID) - if err != nil { - return fmt.Errorf("error inserting row: %w", err) - } - - return nil -} - -// SigningOrderRequestOutbox - model for the signing request outbox -type SigningOrderRequestOutbox struct { - RequestID uuid.UUID `db:"request_id"` - OrderID uuid.UUID `db:"order_id"` - ItemID uuid.UUID `db:"item_id"` - CompletedAt *time.Time `db:"completed_at"` - Message json.RawMessage `db:"message_data" json:"message"` -} - -// GetSigningOrderRequestOutboxByOrder retrieves the latest signing order from the outbox. -func (pg *Postgres) GetSigningOrderRequestOutboxByOrder(ctx context.Context, orderID uuid.UUID) ([]SigningOrderRequestOutbox, error) { - var signingRequestOutbox []SigningOrderRequestOutbox - err := pg.RawDB().SelectContext(ctx, &signingRequestOutbox, - `select request_id, order_id, item_id, completed_at, message_data - from signing_order_request_outbox where order_id = $1`, orderID) - if err != nil { - return nil, fmt.Errorf("error retrieving signing request from outbox: %w", err) - } - return signingRequestOutbox, nil -} - -// GetSigningOrderRequestOutboxByOrderItem retrieves the latest signing order from the outbox. -// An empty result set is returned if no rows are found. -func (pg *Postgres) GetSigningOrderRequestOutboxByOrderItem(ctx context.Context, itemID uuid.UUID) ([]SigningOrderRequestOutbox, error) { - var signingRequestOutbox []SigningOrderRequestOutbox - err := pg.RawDB().SelectContext(ctx, &signingRequestOutbox, - `select request_id, order_id, item_id, completed_at, message_data - from signing_order_request_outbox where item_id = $1`, itemID) - if err != nil { - return nil, fmt.Errorf("error retrieving signing requests from outbox: %w", err) - } - return signingRequestOutbox, nil -} - -// GetSigningOrderRequestOutboxByRequestID retrieves the SigningOrderRequestOutbox by requestID. -// -// An error is returned if the result set is empty. -func (pg *Postgres) GetSigningOrderRequestOutboxByRequestID(ctx context.Context, dbi sqlx.QueryerContext, reqID uuid.UUID) (*SigningOrderRequestOutbox, error) { - const q = `SELECT request_id, order_id, item_id, completed_at, message_data - FROM signing_order_request_outbox - WHERE request_id = $1 FOR UPDATE` - - result := &SigningOrderRequestOutbox{} - if err := sqlx.GetContext(ctx, dbi, result, q, reqID); err != nil { - return nil, fmt.Errorf("error retrieving signing request from outbox: %w", err) - } - - return result, nil -} - -// UpdateSigningOrderRequestOutboxTx updates a signing order request outbox message for the given requestID. -func (pg *Postgres) UpdateSigningOrderRequestOutboxTx(ctx context.Context, tx *sqlx.Tx, requestID uuid.UUID, completedAt time.Time) error { - _, err := tx.ExecContext(ctx, `update signing_order_request_outbox set completed_at = $2 - where request_id = $1`, requestID, completedAt) - if err != nil { - return fmt.Errorf("error updating signing order request: %w", err) - } - return nil -} - -// InsertSigningOrderRequestOutbox inserts the signing order request into the outbox. -func (pg *Postgres) InsertSigningOrderRequestOutbox(ctx context.Context, requestID, orderID, itemID uuid.UUID, signingOrderRequest SigningOrderRequest) error { - message, err := json.Marshal(signingOrderRequest) - if err != nil { - return fmt.Errorf("error marshalling signing order request: %w", err) - } - - const q = `INSERT INTO signing_order_request_outbox (request_id, order_id, item_id, message_data) VALUES ($1, $2, $3, $4)` - if _, err := pg.ExecContext(ctx, q, requestID, orderID, itemID, message); err != nil { - return fmt.Errorf("error inserting order request outbox row: %w", err) - } - - return nil -} - -// DeleteSigningOrderRequestOutboxByOrderTx performs a hard delete of all signing order request outbox -// messages for a given orderID. -func (pg *Postgres) DeleteSigningOrderRequestOutboxByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) error { - _, err := tx.ExecContext(ctx, `delete from signing_order_request_outbox where order_id = $1`, orderID) - if err != nil { - return fmt.Errorf("error deleting signing order request outbox: %w", err) - } - return nil -} - -// SendSigningRequest sends batches of signing order requests to kafka for signing. -// Failed messages are logged and can be manually retried. -// Default batch size 10. -func (pg *Postgres) SendSigningRequest(ctx context.Context, signingRequestWriter SigningRequestWriter) error { - _, tx, rollback, commit, err := datastore.GetTx(ctx, pg) - if err != nil { - return fmt.Errorf("error send signing request could not begin tx: %w", err) - } - defer rollback() - - var soro []SigningOrderRequestOutbox - err = tx.SelectContext(ctx, &soro, `select request_id, order_id, item_id, message_data from signing_order_request_outbox - where submitted_at is null order by created_at asc - for update skip locked limit $1`, signingRequestBatchSize) - if err != nil { - return fmt.Errorf("error could not get signing order request outbox: %w", err) - } - - if len(soro) == 0 { - return nil - } - - // If there is an error writing messages to kafka we need to log the failed messages here instead of returning - // to the job runner, we can then update the messages as processed and continue to the next batch rather than - // retrying, these errors are likely not transient and need checked before retry. - go func() { - switch err := signingRequestWriter.WriteMessages(ctx, soro).(type) { - case nil: - case kafka.WriteErrors: - for i := range soro { - if err[i] != nil { - logging.FromContext(ctx).Err(err[i]). - Interface("message", soro[i]). - Msg("error writing outbox message") - sentry.CaptureException(err) - } - } - default: - logging.FromContext(ctx).Err(err). - Interface("messages", soro). - Msg("error writing outbox messages") - sentry.CaptureException(err) - } - }() - - soroIDs := make([]uuid.UUID, len(soro)) - for i := 0; i < len(soroIDs); i++ { - soroIDs[i] = soro[i].RequestID - } - - qry, args, err := sqlx.In(`update signing_order_request_outbox - set submitted_at = now() where request_id IN (?)`, soroIDs) - if err != nil { - return fmt.Errorf("error creating sql update statement: %w", err) - } - - result, err := tx.ExecContext(ctx, pg.Rebind(qry), args...) - if err != nil { - return fmt.Errorf("error updating outbox message: %w", err) - } - - rows, err := result.RowsAffected() - if err != nil { - return fmt.Errorf("error getting updated outbox message rows: %w", err) - } - - if rows != int64(len(soroIDs)) { - return fmt.Errorf("error updating rows expected %d got %d", len(soroIDs), rows) - } - - err = commit() - if err != nil { - return fmt.Errorf("error committing signing order request outbox: %w", err) - } - - return nil -} - -// InsertSignedOrderCredentialsTx inserts a signed order request. It handles both TimeLimitedV2Creds and -// SingleUse credentials. All SigningOrder's in the SigningOrderResult must be successful to persist the overall result. -func (pg *Postgres) InsertSignedOrderCredentialsTx(ctx context.Context, tx *sqlx.Tx, signedOrderResult *SigningOrderResult) error { - var requestID = signedOrderResult.RequestID - if len(signedOrderResult.Data) == 0 { - return fmt.Errorf("error no signing order result is empty for requestID %s", - signedOrderResult.RequestID) - } - - for _, so := range signedOrderResult.Data { - - var metadata Metadata - err := json.Unmarshal(so.AssociatedData, &metadata) - if err != nil { - return fmt.Errorf("error desearializing associated data for requestID %s: %w", - signedOrderResult.RequestID, err) - } - - if so.Status != SignedOrderStatusOk { - return fmt.Errorf("error signing order creds for orderID %s itemID %s issuerID %s status %s", - metadata.OrderID, metadata.ItemID, metadata.IssuerID, so.Status.String()) - } - - blindedCreds := jsonutils.JSONStringArray(so.BlindedTokens) - if len(blindedCreds) == 0 { - return fmt.Errorf("error blinded tokens is empty order creds orderID %s itemID %s: %w", - metadata.OrderID, metadata.ItemID, err) - } - - signedTokens := jsonutils.JSONStringArray(so.SignedTokens) - if len(signedTokens) == 0 { - return fmt.Errorf("error signed tokens is empty order creds orderID %s itemID %s: %w", - metadata.OrderID, metadata.ItemID, err) - } - - switch metadata.CredentialType { - - case singleUse: - - cred := &OrderCreds{ - ID: metadata.ItemID, - OrderID: metadata.OrderID, - IssuerID: metadata.IssuerID, - BlindedCreds: blindedCreds, - SignedCreds: &signedTokens, - BatchProof: ptr.FromString(so.Proof), - PublicKey: ptr.FromString(so.PublicKey), - } - - err = pg.InsertOrderCredsTx(ctx, tx, cred) - if err != nil { - return fmt.Errorf("error inserting single use order credential orderID %s itemID %s: %w", - metadata.OrderID, metadata.ItemID, err) - } - - case timeLimitedV2: - if so.ValidTo.Value() == nil { - return fmt.Errorf("error validTo for order creds orderID %s itemID %s is null: %w", - metadata.OrderID, metadata.ItemID, err) - } - - validTo, err := time.Parse(time.RFC3339, *so.ValidTo.Value()) - if err != nil { - return fmt.Errorf("error parsing validTo for order creds orderID %s itemID %s: %w", - metadata.OrderID, metadata.ItemID, err) - } - - if so.ValidFrom.Value() == nil { - return fmt.Errorf("error validFrom for order creds orderID %s itemID %s is null: %w", - metadata.OrderID, metadata.ItemID, err) - } - - validFrom, err := time.Parse(time.RFC3339, *so.ValidFrom.Value()) - if err != nil { - return fmt.Errorf("error parsing validFrom for order creds orderID %s itemID %s: %w", - metadata.OrderID, metadata.ItemID, err) - } - - o, err := pg.GetOrder(metadata.OrderID) - if err != nil { - return fmt.Errorf("failed to get the order %s: %w", metadata.OrderID, err) - } - if o.ExpiresAt == nil || validFrom.After(*o.ExpiresAt) { - // filter out creds after the order expires - continue - } - - cred := TimeAwareSubIssuedCreds{ - ItemID: metadata.ItemID, - OrderID: metadata.OrderID, - IssuerID: metadata.IssuerID, - BlindedCreds: blindedCreds, - SignedCreds: signedTokens, - BatchProof: so.Proof, - PublicKey: so.PublicKey, - ValidTo: validTo, - ValidFrom: validFrom, - RequestID: requestID, - } - - err = pg.InsertTimeLimitedV2OrderCredsTx(ctx, tx, cred) - if err != nil { - return fmt.Errorf("error inserting time limited order credential orderID %s itemID %s: %w", - metadata.OrderID, metadata.ItemID, err) - } - - default: - return fmt.Errorf("error unknown credential type %s for order credential orderID %s itemID %s", - metadata.CredentialType, metadata.OrderID, metadata.ItemID) - } - } - - return nil -} - -// AppendOrderMetadataInt64 appends the key and int64 value to an order's metadata. -func (pg *Postgres) AppendOrderMetadataInt64(ctx context.Context, orderID *uuid.UUID, key string, value int64) error { - _, tx, rollback, commit, err := datastore.GetTx(ctx, pg) - if err != nil { - return err - } - defer rollback() - - if err := pg.orderRepo.AppendMetadataInt64(ctx, tx, *orderID, key, value); err != nil { - return fmt.Errorf("error updating order metadata %s: %w", orderID, err) - } - - return commit() -} - -// AppendOrderMetadataInt appends the key and int value to an order's metadata. -func (pg *Postgres) AppendOrderMetadataInt(ctx context.Context, orderID *uuid.UUID, key string, value int) error { - _, tx, rollback, commit, err := datastore.GetTx(ctx, pg) - if err != nil { - return err - } - defer rollback() - - if err := pg.orderRepo.AppendMetadataInt(ctx, tx, *orderID, key, value); err != nil { - return fmt.Errorf("error updating order metadata %s: %w", orderID, err) - } - - return commit() -} - -// AppendOrderMetadata appends the key and string value to an order's metadata. -func (pg *Postgres) AppendOrderMetadata(ctx context.Context, orderID *uuid.UUID, key, value string) error { - _, tx, rollback, commit, err := datastore.GetTx(ctx, pg) - if err != nil { - return err - } - defer rollback() - - if err := pg.orderRepo.AppendMetadata(ctx, tx, *orderID, key, value); err != nil { - return fmt.Errorf("error updating order metadata %s: %w", orderID, err) - } - - return commit() -} - -// SetOrderPaid sets status to paid for the order, updates last paid and expiration. -func (pg *Postgres) SetOrderPaid(ctx context.Context, orderID *uuid.UUID) error { - _, tx, rollback, commit, err := datastore.GetTx(ctx, pg) - if err != nil { - return fmt.Errorf("failed to get db transaction: %w", err) - } - defer rollback() - - if err := pg.orderRepo.SetStatus(ctx, tx, *orderID, OrderStatusPaid); err != nil { - return fmt.Errorf("error updating order %s: %w", orderID, err) - } - - if err := pg.recordOrderPayment(ctx, tx, *orderID, time.Now()); err != nil { - return fmt.Errorf("failed to record order payment: %w", err) - } - - if err := pg.updateOrderExpiresAt(ctx, tx, *orderID); err != nil { - return fmt.Errorf("failed to set order expires_at: %w", err) - } - - return commit() -} - -func (pg *Postgres) recordOrderPayment(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error { - if err := pg.orderPayHistory.Insert(ctx, dbi, id, when); err != nil { - return err - } - - return pg.orderRepo.SetLastPaidAt(ctx, dbi, id, when) -} - -func (pg *Postgres) updateOrderExpiresAt(ctx context.Context, dbi sqlx.ExtContext, orderID uuid.UUID) error { - expiresAt, err := pg.orderRepo.GetExpiresAtAfterISOPeriod(ctx, dbi, orderID) - if err != nil { - return fmt.Errorf("unable to get order time bounds: %w", err) - } - - return pg.orderRepo.SetExpiresAt(ctx, dbi, orderID, expiresAt) -} diff --git a/services/skus/datastore_test.go b/services/skus/datastore_test.go deleted file mode 100644 index 6ff26089c..000000000 --- a/services/skus/datastore_test.go +++ /dev/null @@ -1,666 +0,0 @@ -//go:build integration - -package skus - -import ( - "context" - "database/sql" - "encoding/json" - "os" - "strings" - "testing" - "time" - - "github.com/DATA-DOG/go-sqlmock" - "github.com/golang/mock/gomock" - "github.com/jmoiron/sqlx" - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - must "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/jsonutils" - "github.com/brave-intl/bat-go/libs/ptr" - "github.com/brave-intl/bat-go/libs/test" - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/skustest" - - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -type PostgresTestSuite struct { - suite.Suite - storage Datastore -} - -func TestPostgresTestSuite(t *testing.T) { - suite.Run(t, new(PostgresTestSuite)) -} - -func (suite *PostgresTestSuite) SetupSuite() { - skustest.Migrate(suite.T()) - storage, _ := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - - suite.storage = storage -} - -func (suite *PostgresTestSuite) SetupTest() { - skustest.CleanDB(suite.T(), suite.storage.RawDB()) -} - -func TestGetPagedMerchantTransactions(t *testing.T) { - ctx := context.Background() - // setup mock DB we will inject into our pg - mockDB, mock, err := sqlmock.New() - if err != nil { - t.Errorf("failed to create a sql mock: %s", err) - } - defer func() { - if err := mockDB.Close(); err != nil { - if !strings.Contains(err.Error(), "all expectations were already fulfilled") { - t.Errorf("failed to close the mock database: %s", err) - } - } - }() - - pg := &Postgres{ - Postgres: datastore.Postgres{DB: sqlx.NewDb(mockDB, "sqlmock")}, - orderRepo: repository.NewOrder(), - orderItemRepo: repository.NewOrderItem(), - orderPayHistory: repository.NewOrderPayHistory(), - } - - // setup inputs - merchantID := uuid.NewV4() - ctx, pagination, err := inputs.NewPagination(ctx, "/?page=2&items=50&order=id.asc&order=createdAt.desc", new(Transaction)) - if err != nil { - t.Errorf("failed to create pagination: %s\n", err) - } - - // setup expected mocks - countRows := sqlmock.NewRows([]string{"total"}).AddRow(3) - mock.ExpectQuery(` - SELECT (.+) as total - FROM transactions as t - INNER JOIN orders as o ON o.id = t.order_id - WHERE (.+)`).WithArgs(merchantID).WillReturnRows(countRows) - - transactionUUIDs := []uuid.UUID{uuid.NewV4(), uuid.NewV4(), uuid.NewV4()} - orderUUIDs := []uuid.UUID{uuid.NewV4(), uuid.NewV4(), uuid.NewV4()} - createdAt := []time.Time{time.Now(), time.Now().Add(time.Second * 5), time.Now().Add(time.Second * 10)} - - getRows := sqlmock.NewRows( - []string{"id", "order_id", "created_at", "updated_at", - "external_transaction_id", "status", "currency", "kind", "amount"}). - AddRow(transactionUUIDs[0], orderUUIDs[0], createdAt[0], createdAt[0], "", "pending", "BAT", "subscription", 10). - AddRow(transactionUUIDs[1], orderUUIDs[1], createdAt[1], createdAt[1], "", "pending", "BAT", "subscription", 10). - AddRow(transactionUUIDs[2], orderUUIDs[2], createdAt[2], createdAt[2], "", "pending", "BAT", "subscription", 10) - - mock.ExpectQuery(` - SELECT (.+) - FROM transactions as t - INNER JOIN orders as o ON o.id = t.order_id - WHERE o.merchant_id = (.+) - ORDER BY (.+) OFFSET (.+) FETCH NEXT (.+)`).WithArgs(merchantID). - WillReturnRows(getRows) - - // call function under test with inputs - transactions, c, err := pg.GetPagedMerchantTransactions(ctx, merchantID, pagination) - - // test assertions - if err != nil { - t.Errorf("failed to get paged merchant transactions: %s\n", err) - } - if len(*transactions) != 3 { - t.Errorf("should have seen 3 transactions: %+v\n", transactions) - } - if c != 3 { - t.Errorf("should have total count of 3 transactions: %d\n", c) - } -} - -func (suite *PostgresTestSuite) TestGetOrderByExternalID() { - ctx := context.Background() - defer ctx.Done() - - // create an issuer and a paid order with one order item and a time limited v2 credential type. - ctx = context.WithValue(context.Background(), appctx.WhitelistSKUsCTXKey, []string{devFreeTimeLimitedV2}) - o1, _ := createOrderAndIssuer(suite.T(), ctx, suite.storage, devFreeTimeLimitedV2) - - // add the external id to metadata - err := suite.storage.AppendOrderMetadata(ctx, &o1.ID, "externalID", "my external id") - suite.Require().NoError(err) - - // test out get by external id - o2, err := suite.storage.GetOrderByExternalID("my external id") - suite.Require().NoError(err) - suite.Assert().NotNil(o2) - - if o2 != nil { - suite.Assert().Equal(o2.ID.String(), o1.ID.String()) - } -} - -func (suite *PostgresTestSuite) TestGetTimeLimitedV2OrderCredsByOrder_Success() { - env := os.Getenv("ENV") - ctx := context.WithValue(context.Background(), appctx.EnvironmentCTXKey, env) - - // create paid order with two order items - ctx = context.WithValue(ctx, appctx.WhitelistSKUsCTXKey, []string{devBraveFirewallVPNPremiumTimeLimited, - devBraveSearchPremiumYearTimeLimited}) - - orderCredentials := suite.createTimeLimitedV2OrderCreds(suite.T(), ctx, devBraveFirewallVPNPremiumTimeLimited, - devBraveSearchPremiumYearTimeLimited) - - // create an unrelated order which should not be returned - _ = suite.createTimeLimitedV2OrderCreds(suite.T(), ctx, devBraveSearchPremiumYearTimeLimited) - - // both order items have same orderID so can use the first element to retrieve all order creds - actual, err := suite.storage.GetTimeLimitedV2OrderCredsByOrder(orderCredentials[0].OrderID) - suite.Require().NoError(err) - - suite.Assert().Equal(orderCredentials[0].OrderID, actual.OrderID) - suite.Assert().Equal(orderCredentials[0].IssuerID, actual.IssuerID) - // this will contain all the order creds for each of the order items - suite.Assert().ElementsMatch(orderCredentials, actual.Credentials) -} - -func (suite *PostgresTestSuite) TestGetTimeLimitedV2OrderCredsByOrderItem_Success() { - env := os.Getenv("ENV") - ctx := context.WithValue(context.Background(), appctx.EnvironmentCTXKey, env) - - // create paid order with two order items - ctx = context.WithValue(ctx, appctx.WhitelistSKUsCTXKey, []string{devBraveFirewallVPNPremiumTimeLimited, - devBraveSearchPremiumYearTimeLimited}) - - orderCredentials := suite.createTimeLimitedV2OrderCreds(suite.T(), ctx, devBraveFirewallVPNPremiumTimeLimited, - devBraveSearchPremiumYearTimeLimited) - - // create an unrelated order which should not be returned - _ = suite.createTimeLimitedV2OrderCreds(suite.T(), ctx, devBraveSearchPremiumYearTimeLimited) - - // retrieve the first order credential from our newly created order items - actual, err := suite.storage.GetTimeLimitedV2OrderCredsByOrderItem(orderCredentials[0].ItemID) - suite.Require().NoError(err) - - suite.Assert().Equal(orderCredentials[0].OrderID, actual.OrderID) - suite.Assert().Equal(orderCredentials[0].IssuerID, actual.IssuerID) - - // credentials should only contain the order cred for the first item we retrieved - suite.Assert().Equal(1, len(actual.Credentials)) - suite.Assert().ElementsMatch(orderCredentials[0:1], actual.Credentials) -} - -func (suite *PostgresTestSuite) TestSendSigningRequest_MultipleRow_Success() { - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - ctx := context.Background() - - // Insert multiple messages for processing - - orderID := uuid.NewV4() - - for i := 0; i < signingRequestBatchSize+1; i++ { - metadata := Metadata{ - ItemID: uuid.NewV4(), - OrderID: orderID, - IssuerID: uuid.NewV4(), - CredentialType: test.RandomString(), - } - - associatedData, err := json.Marshal(metadata) - suite.Require().NoError(err) - - requestID := uuid.NewV4() - - signingOrderRequest := SigningOrderRequest{ - RequestID: requestID.String(), - Data: []SigningOrder{ - { - IssuerType: test.RandomString(), - IssuerCohort: defaultCohort, - BlindedTokens: []string{test.RandomString()}, - AssociatedData: associatedData, - }, - }, - } - - err = suite.storage.InsertSigningOrderRequestOutbox(context.Background(), requestID, metadata.OrderID, - metadata.ItemID, signingOrderRequest) - suite.Require().NoError(err) - } - - // Capture the messages that are picked up. We need to do this as the query - // just picks up any non-processed messages. - // We should only process the max batch size. - - var messagesItemID []uuid.UUID - - signingRequestWriter := NewMockSigningRequestWriter(ctrl) - signingRequestWriter.EXPECT(). - WriteMessages(gomock.Any(), gomock.Len(signingRequestBatchSize)). - Do(func(ctx context.Context, messages []SigningOrderRequestOutbox) { - for _, message := range messages { - messagesItemID = append(messagesItemID, message.ItemID) - } - }). - Times(1). - Return(nil) - - err := suite.storage.SendSigningRequest(ctx, signingRequestWriter) - suite.Require().NoError(err) - - // Assert kafka mock was called with signing requests - suite.Require().NotNil(messagesItemID) - - // Assert that all the messages picked up have been marked as processed - - qry, args, err := sqlx.In(`select order_id, submitted_at from signing_order_request_outbox where item_id IN (?)`, - messagesItemID) - suite.Require().NoError(err) - - type outboxMessage struct { - OrderID uuid.UUID `db:"order_id"` - SubmittedAt *time.Time `db:"submitted_at"` - } - - var actual []outboxMessage - - err = suite.storage.RawDB().SelectContext(ctx, &actual, suite.storage.RawDB().Rebind(qry), args...) - suite.Require().NoError(err) - - for _, s := range actual { - suite.Assert().NotNil(s.SubmittedAt) - } -} - -func (suite *PostgresTestSuite) TestSendSigningRequest_NoRows() { - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - ctx := context.Background() - - // should not be called - signingRequestWriter := NewMockSigningRequestWriter(ctrl) - signingRequestWriter.EXPECT(). - WriteMessages(ctx, gomock.Any()). - Return(nil). - Times(0) - - err := suite.storage.SendSigningRequest(ctx, signingRequestWriter) - suite.Require().NoError(err) -} - -func (suite *PostgresTestSuite) TestInsertSignedOrderCredentials_SingleUse_Success() { - ctx := context.Background() - - // create an issuer and a paid order with one order item and a single use credential type. - ctx = context.WithValue(context.Background(), appctx.WhitelistSKUsCTXKey, []string{devUserWalletVote}) - order, issuer := createOrderAndIssuer(suite.T(), ctx, suite.storage, devUserWalletVote) - - metadata := Metadata{ - ItemID: order.Items[0].ID, - OrderID: order.ID, - IssuerID: issuer.ID, - CredentialType: order.Items[0].CredentialType, - } - - associatedData, err := json.Marshal(metadata) - suite.Require().NoError(err) - - signingOrderResult := &SigningOrderResult{ - RequestID: uuid.NewV4().String(), - Data: []SignedOrder{ - { - PublicKey: issuer.PublicKey, - Proof: test.RandomString(), - Status: SignedOrderStatusOk, - BlindedTokens: []string{test.RandomString()}, - SignedTokens: []string{test.RandomString()}, - AssociatedData: associatedData, - }, - }, - } - - ctx, tx, rollback, commit, err := datastore.GetTx(ctx, suite.storage) - defer rollback() - - err = suite.storage.InsertSignedOrderCredentialsTx(ctx, tx, signingOrderResult) - suite.Require().NoError(err) - - err = commit() - suite.Require().NoError(err) - - time.Sleep(time.Millisecond) - - actual, err := suite.storage.GetOrderCredsByItemID(order.ID, order.Items[0].ID, false) - suite.Require().NoError(err) - - suite.Require().NotNil(actual) - suite.Assert().Equal(signingOrderResult.Data[0].PublicKey, *actual.PublicKey) - suite.Assert().Equal(signingOrderResult.Data[0].Proof, *actual.BatchProof) - suite.Assert().Equal(jsonutils.JSONStringArray(signingOrderResult.Data[0].SignedTokens), *actual.SignedCreds) -} - -func (suite *PostgresTestSuite) TestInsertSignedOrderCredentials_TimeAwareV2_Success() { - ctx := context.Background() - - // create an issuer and a paid order with one order item and a time limited v2 credential type. - ctx = context.WithValue(context.Background(), appctx.WhitelistSKUsCTXKey, []string{devFreeTimeLimitedV2}) - order, issuer := createOrderAndIssuer(suite.T(), ctx, suite.storage, devFreeTimeLimitedV2) - - metadata := Metadata{ - ItemID: order.Items[0].ID, - OrderID: order.ID, - IssuerID: issuer.ID, - CredentialType: order.Items[0].CredentialType, - } - - associatedData, err := json.Marshal(metadata) - suite.Require().NoError(err) - - vFrom := time.Now().Local().Format(time.RFC3339) - vTo := time.Now().Local().Add(time.Hour).Format(time.RFC3339) - - signingOrderResult := &SigningOrderResult{ - RequestID: uuid.NewV4().String(), - Data: []SignedOrder{ - { - PublicKey: issuer.PublicKey, - Proof: test.RandomString(), - Status: SignedOrderStatusOk, - BlindedTokens: []string{test.RandomString()}, - SignedTokens: []string{test.RandomString()}, - ValidTo: &UnionNullString{"string": vTo}, - ValidFrom: &UnionNullString{"string": vFrom}, - AssociatedData: associatedData, - }, - }, - } - - ctx, tx, rollback, commit, err := datastore.GetTx(ctx, suite.storage) - defer rollback() - - err = suite.storage.InsertSignedOrderCredentialsTx(ctx, tx, signingOrderResult) - suite.Require().NoError(err) - - err = commit() - suite.Require().NoError(err) - - time.Sleep(time.Millisecond) - - actual, err := suite.storage.GetTimeLimitedV2OrderCredsByOrderItem(order.Items[0].ID) - suite.Require().NoError(err) - - suite.Require().NotNil(actual) - suite.Assert().Equal(signingOrderResult.Data[0].PublicKey, actual.Credentials[0].PublicKey) - suite.Assert().Equal(signingOrderResult.Data[0].Proof, actual.Credentials[0].BatchProof) - suite.Assert().Equal(jsonutils.JSONStringArray(signingOrderResult.Data[0].SignedTokens), actual.Credentials[0].SignedCreds) - suite.Assert().Equal(jsonutils.JSONStringArray(signingOrderResult.Data[0].BlindedTokens), actual.Credentials[0].BlindedCreds) - - to, err := time.Parse(time.RFC3339, vTo) - suite.Require().NoError(err) - - from, err := time.Parse(time.RFC3339, vFrom) - suite.Require().NoError(err) - - suite.Assert().Equal(to, actual.Credentials[0].ValidTo) - suite.Assert().Equal(from, actual.Credentials[0].ValidFrom) -} - -func (suite *PostgresTestSuite) TestInsertSigningOrderRequestOutbox() { - orderID := uuid.NewV4() - itemID := uuid.NewV4() - - requestID := uuid.NewV4() - - signingOrderRequest := SigningOrderRequest{ - RequestID: requestID.String(), - Data: []SigningOrder{ - { - IssuerType: test.RandomString(), - IssuerCohort: defaultCohort, - BlindedTokens: []string{test.RandomString()}, - AssociatedData: []byte{}, - }, - }, - } - - ctx := context.Background() - - err := suite.storage.InsertSigningOrderRequestOutbox(ctx, requestID, orderID, itemID, signingOrderRequest) - suite.Require().NoError(err) - - signingOrderRequests, err := suite.storage.GetSigningOrderRequestOutboxByOrderItem(ctx, itemID) - suite.Require().NoError(err) - - suite.Require().Len(signingOrderRequests, 1) - - suite.Assert().Equal(orderID, signingOrderRequests[0].OrderID) - suite.Assert().Equal(itemID, signingOrderRequests[0].ItemID) - - var actual SigningOrderRequest - err = json.Unmarshal(signingOrderRequests[0].Message, &actual) - suite.Assert().NoError(err) - suite.Assert().Equal(signingOrderRequest, actual) -} - -//nolint:typecheck -func createOrderAndIssuer(t *testing.T, ctx context.Context, storage Datastore, sku ...string) (*Order, *Issuer) { - var ( - svc = &Service{} - orderItems []OrderItem - methods []string - ) - - for _, s := range sku { - orderItem, method, _, err := svc.CreateOrderItemFromMacaroon(ctx, s, 1) - must.NoError(t, err) - - orderItems = append(orderItems, *orderItem) - methods = append(methods, method...) - } - - validFor := 3600 * time.Second * 24 - - oreq := &model.OrderNew{ - MerchantID: test.RandomString(), - Currency: test.RandomString(), - Status: model.OrderStatusPaid, - TotalPrice: decimal.NewFromInt(int64(test.RandomInt())), - Location: sql.NullString{ - Valid: true, - String: test.RandomString(), - }, - AllowedPaymentMethods: pq.StringArray(methods), - ValidFor: &validFor, - } - - order, err := storage.CreateOrder(ctx, storage.RawDB(), oreq, orderItems) - must.NoError(t, err) - - { - err := storage.UpdateOrder(order.ID, OrderStatusPaid) - must.NoError(t, err) - } - - repo := repository.NewIssuer() - issuer, err := repo.Create(ctx, storage.RawDB(), model.IssuerNew{ - MerchantID: test.RandomString(), - PublicKey: test.RandomString(), - }) - must.NoError(t, err) - - return order, issuer -} - -// helper to setup a paid order, order items, issuer and insert time limited v2 order credentials -func (suite *PostgresTestSuite) createTimeLimitedV2OrderCreds(t *testing.T, ctx context.Context, sku ...string) []TimeAwareSubIssuedCreds { - var ( - svc = Service{} - orderItems []OrderItem - methods []string - ) - - for _, s := range sku { - orderItem, method, _, err := svc.CreateOrderItemFromMacaroon(ctx, s, 1) - must.NoError(t, err) - - orderItems = append(orderItems, *orderItem) - methods = append(methods, method...) - } - - oreq := &model.OrderNew{ - MerchantID: test.RandomString(), - Currency: test.RandomString(), - Status: model.OrderStatusPaid, - TotalPrice: decimal.NewFromInt(int64(test.RandomInt())), - Location: sql.NullString{ - Valid: true, - String: test.RandomString(), - }, - AllowedPaymentMethods: pq.StringArray(methods), - } - - order, err := suite.storage.CreateOrder(ctx, suite.storage.RawDB(), oreq, orderItems) - must.NoError(t, err) - - repo := repository.NewIssuer() - - issuer, err := repo.Create(ctx, suite.storage.RawDB(), model.IssuerNew{ - MerchantID: test.RandomString(), - PublicKey: test.RandomString(), - }) - must.NoError(t, err) - - // create the time limited order credentials for each of the order items in our order - to := time.Now().Add(time.Hour).Format(time.RFC3339) - validTo, err := time.Parse(time.RFC3339, to) - must.NoError(t, err) - - from := time.Now().Local().Format(time.RFC3339) - validFrom, err := time.Parse(time.RFC3339, from) - must.NoError(t, err) - - signedCreds := jsonutils.JSONStringArray([]string{test.RandomString()}) - - var orderCredentials []TimeAwareSubIssuedCreds - - _, tx, rollback, commit, err := datastore.GetTx(ctx, suite.storage) - suite.Require().NoError(err) - - defer rollback() - - for _, orderItem := range order.Items { - tlv2 := TimeAwareSubIssuedCreds{ - ItemID: orderItem.ID, - OrderID: order.ID, - IssuerID: issuer.ID, - BlindedCreds: []string{test.RandomString()}, - SignedCreds: signedCreds, - BatchProof: test.RandomString(), - PublicKey: issuer.PublicKey, - ValidTo: validTo, - ValidFrom: validFrom, - } - - err := suite.storage.InsertTimeLimitedV2OrderCredsTx(ctx, tx, tlv2) - must.NoError(t, err) - - orderCredentials = append(orderCredentials, tlv2) - } - - { - err := commit() - suite.Require().NoError(err) - } - - return orderCredentials -} - -// helper to setup a paid order, order items, issuer and insert unsigned order credentials -func (suite *PostgresTestSuite) createOrderCreds(t *testing.T, ctx context.Context, sku ...string) []*OrderCreds { - var ( - svc = Service{} - orderItems []OrderItem - methods []string - ) - - for _, s := range sku { - orderItem, method, _, err := svc.CreateOrderItemFromMacaroon(ctx, s, 1) - must.NoError(t, err) - - orderItems = append(orderItems, *orderItem) - methods = append(methods, method...) - } - - oreq := &model.OrderNew{ - MerchantID: test.RandomString(), - Currency: test.RandomString(), - Status: model.OrderStatusPaid, - TotalPrice: decimal.NewFromInt(int64(test.RandomInt())), - Location: sql.NullString{ - Valid: true, - String: test.RandomString(), - }, - AllowedPaymentMethods: pq.StringArray(methods), - } - - order, err := suite.storage.CreateOrder(ctx, suite.storage.RawDB(), oreq, orderItems) - must.NoError(t, err) - - pk := test.RandomString() - - repo := repository.NewIssuer() - - issuer, err := repo.Create(ctx, suite.storage.RawDB(), model.IssuerNew{ - MerchantID: test.RandomString(), - PublicKey: pk, - }) - must.NoError(t, err) - - signedCreds := jsonutils.JSONStringArray([]string{test.RandomString()}) - - var orderCredentials []*OrderCreds - - _, tx, rollback, commit, err := datastore.GetTx(ctx, suite.storage) - suite.Require().NoError(err) - - defer rollback() - - // insert order creds - for _, orderItem := range order.Items { - oc := &OrderCreds{ - ID: orderItem.ID, // item_id - OrderID: order.ID, - IssuerID: issuer.ID, - BlindedCreds: []string{test.RandomString()}, - SignedCreds: &signedCreds, - BatchProof: ptr.FromString(test.RandomString()), - PublicKey: ptr.FromString(pk), - } - - err := suite.storage.InsertOrderCredsTx(ctx, tx, oc) - must.NoError(t, err) - - orderCredentials = append(orderCredentials, oc) - } - - { - err := commit() - suite.Require().NoError(err) - } - - return orderCredentials -} diff --git a/services/skus/docker-compose.payment-refresh.yml b/services/skus/docker-compose.payment-refresh.yml deleted file mode 100644 index c48fee9d5..000000000 --- a/services/skus/docker-compose.payment-refresh.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: "3.4" - -services: - # payment-refresh service will start up a grant server bound to host port 3333 - # which allows one to do `docker restart grant-payment-refresh` when the user - # wants to "restart" the service running new code. This is especially helpful - # when you hook it up to `fswatch` type utilities, causing a re-run of `go run` - # every time a file changes. - payment-refresh: - container_name: grant-payment-refresh - image: golang:1.19 - ports: - - "3335:3333" - - "6061:6061" - # payments runs in grants right now, not split out into own command yet - command: "go run main.go serve grant" - volumes: - - .:/src - - ./test/secrets:/etc/kafka/secrets - working_dir: /src - depends_on: - - kafka - - postgres - - challenge-bypass - networks: - - grant - environment: - - ADDR=:3333 - - PPROF_ENABLED=true - - ENABLE_LINKING_DRAINING=true - - ENV=local - - DEBUG=1 - - BAT_SETTLEMENT_ADDRESS - - BRAVE_TRANSFER_PROMOTION_ID - - CHALLENGE_BYPASS_SERVER=http://challenge-bypass:2416 - - CHALLENGE_BYPASS_TOKEN - - FEATURE_WALLET=true - - "DATABASE_MIGRATIONS_URL=file:///src/migrations" - - "DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable" - - ENCRYPTION_KEY=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0 - - FEATURE_PAYMENT=true - - DONOR_WALLET_CARD_ID - - DONOR_WALLET_PRIVATE_KEY - - DONOR_WALLET_PUBLIC_KEY - - GRANT_SIGNATOR_PUBLIC_KEY - - GRANT_WALLET_CARD_ID - - GRANT_WALLET_PRIVATE_KEY - - GRANT_WALLET_PUBLIC_KEY - - KAFKA_BROKERS=kafka:19092 - - KAFKA_SSL_CA_LOCATION=/etc/kafka/secrets/snakeoil-ca-1.crt - - KAFKA_SSL_CERTIFICATE_LOCATION=/etc/kafka/secrets/consumer-ca1-signed.pem - - KAFKA_SSL_KEY_LOCATION=/etc/kafka/secrets/consumer.client.key - - KAFKA_SSL_KEY_PASSWORD=confluent - - KAFKA_REQUIRED_ACKS=1 - - TOKEN_LIST - - UPHOLD_ACCESS_TOKEN - - "RATIOS_SERVICE=https://ratios.rewards.bravesoftware.com" - - RATIOS_TOKEN diff --git a/services/skus/docker-compose.yml b/services/skus/docker-compose.yml deleted file mode 100644 index c01ca11aa..000000000 --- a/services/skus/docker-compose.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: "3.4" - -networks: - skus: - driver: bridge - -services: - # dev-refresh service will start up a rewards server bound to host port 3343 - # which allows one to do `docker restart rewards-dev-refresh` when the user - # wants to "restart" the service running new code. This is especially helpful - # when you hook it up to `fswatch` type utilities, causing a re-run of `go run` - # every time a file changes. - skus-dev-refresh: - container_name: skus-dev-refresh - image: golang:1.19 - ports: - - "3353:3353" - - "6061:6061" - command: "go run main.go serve skus rest" - volumes: - - ../:/src - - ../test/secrets:/etc/kafka/secrets - working_dir: /src - networks: - - skus - - grant - depends_on: - - postgres - environment: - - PPROF_ENABLED=true - - ENV=local - - DEBUG=1 - - CHALLENGE_BYPASS_SERVER=http://challenge-bypass:2416 - - CHALLENGE_BYPASS_TOKEN - - "DATABASE_MIGRATIONS_URL=file:///src/migrations" - - "DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable" - - ENCRYPTION_KEY=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0 - - KAFKA_BROKERS=kafka:19092 - - KAFKA_SSL_CA_LOCATION=/etc/kafka/secrets/snakeoil-ca-1.crt - - KAFKA_SSL_CERTIFICATE_LOCATION=/etc/kafka/secrets/consumer-ca1-signed.pem - - KAFKA_SSL_KEY_LOCATION=/etc/kafka/secrets/consumer.client.key - - KAFKA_SSL_KEY_PASSWORD=confluent - - KAFKA_REQUIRED_ACKS=1 - - TOKEN_LIST diff --git a/services/skus/docs/StripeSKUsIntegration.pdf b/services/skus/docs/StripeSKUsIntegration.pdf deleted file mode 100644 index 5e57fdf24dba4c29cbc8cb864605ed8fa5a4c5b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45035 zcmb?@1ymJU(5`fM_n|@Z(A_N!QX<{mB_Q1?T>^rD(j^!m9TJjCw;%%22#D~G2k!Im z%YU!F_3pas0>hk{{p~&bo7uBBmsV9uh6TvVg-koTvA&251h4}fO>B^bgxDlq%#Ga~ zT>uP{RxXbA*6#L<$ZV=Ej%Mzr<{+Vlt+g>5=(7NOAUhEB8?d&uql1*On>m0%N`Ql% zgM*!iodd`X1ah-8v9r^I{srkNIhy^uiiilZxr5oyF>wAGi>WaHh|DIT1YlEkbg?(K z`=!M7OG#4vr_!aWq9lMFnN3jv{9kPVC&;lP00IRx^dt|TdE&XW+(rzrj`O5UQ zTSV@6=b^*{Xzs|6qjn?)2$95W0RXWb&H=vugX?{67%yKJ)+H~6*Nb_16;+PkEq?Cv z>gDr+Pj5RH<|#g|YbVl_wr;~+HU))dkc$;mW^>FcHE(Tk3ec$^F)`91*s^m(_f4Ci!U&I|)4jU@RdoVC55ZN{j z#uK(hJCX_F5}2dS5#Be6C{10T<0gFXzx$-r&;g3JA4_#gvXzy1Ci571B3vZpbcBdH zS|Db+M%S$_ij{a*RD+4s7IwBkru=)}=b4qQ#cj-Cp0vubjB^zp=9Fmd4sF1 zon)B6r1V%p<*S~eF^R>~fHp@Fa)UE(?~}bwPsfV`v^?#~Ip6)Gouww(*-y(VR?VMF zn62VqFzkj{DYntss?%Eu<1=1|P{ho2pW4~^tw1Z%zHaSXc8YmF}myyr|};Hzq&-mg~Sp|51~xFEiwbqrb=JGdk^C#AGI8 zvdw4X{=4?aV-_OWM6q{uO)XN=hZPIG=WXFNJ=DLQT8ZRmU}RxQtj5xHq&H@E=y?k2 z$Cg>)AG4qxaFHjM5?GgU*4kC(ap>re;H_w->1k(KS9I%PVrz46;n3O!KhQcMZo1#Y z(96POBDUuyQ*B8|JISqwaHm6qldLpWjW}uNC3`4^;!-ikryMeKUVNnBSbd=Zy00cY zy`}K+p@gnpF-m7dJk>c$c5@RQhCOumTcP`7YDOR6YwUd2!a7syha;xae8;shGug** zOSW2oGIWThiAex{v=&La%HUivc&WgU(~divRdnjDrPM_wrM`u3M0Jao6>ihS8CM^2 z@p#YcI(SJ6OUZ2C!?o|rtE0QGyPCr0KYsPfmxe1#j`b%#F+EBiL~IL&E}&75S;yUH zxIfl=`f=-iSr8MnkC;j^Tp}BONLgy@G~ub~qXf>~toM1TR1-6i3L~7rJGl9gJ&$!X zTgXj#8u{j@iH@){Rl=;{fNoZBsH^_T+nhn4-;6(plQUTQ7((fzQ}FqjhYXMmhJS8V zSmY#g)}V{Br(!{LsZI53BrcVUo}mQ$fLr!Ps+>Q-u+c$4_`En{!s2#rE722zDTR=E z<`*1YrzPdtk%~cmeqR@DN5k}qA4#D$zoQb$;2jL1p$vaV!s_s5_bB=kFMc5bj6HR$ z*}JYDOLJTm$5nZmk71rFHi8&s-^I{}y_IQSq%RM9IApjwvCjOb=-BkoNO49ela|I#MdV;Ph&vg>q8QY|_qhpc31*PHQC=O^Ab} zRfuO{q8=+VQJ+Rbs6%{R!`f2i$ZRO7Vr+D7-6yKun5|)rRN*uhlFyNt>z$sRiEM8^ zJz{-Ib7=pbRy5=BzW2wmBmLtd7j<8{VCSLa1S>3M1!Psy=I1Vz;fpA_E<=m&UpGTB z<+n(}JahaSM$Exn-~GjbHlfE+t)n?(S!Q?hzJB_UnKY8KN&}2A$!i-!Cs%O>6GH;B zNc?0ybbI~aS!`=>7bTvIuz$XF=Zihsn{wlG{e0_vXBT-Bq8G{cU0OAeJ7H$~@1tufDRc z?_^oeA!+;{-p9`sk3i~6n4NP`iBAtbUbc6C~jlo%i-|eb=dJ4F!DNMw?B?2|_|U#aZz%c6S_Rf|A2kN_s&yG7 zjw?Elv=1((eOlGFw9BfmTfme9MIy0p509Dn4L!aKv?I)&QYCh5t+1Wm7n^jIbXKaV zxCJ9sC5bSyoI?SashTToqlhJBlspxps=BeK9Wl@tn4!4L$1V7+!%nd8re4{|C7QrQ z8DPtkTPZLEuoG41DG}0Yxxz(12pdedl~eo_b1#-NQ6%h94N(mFe;eT4fF?*v(zJ)G_|2T{b_sO}u;!ci-4VCgN*lxU*-fuXVyDs~m7;0Fbz zwJ@*p-zV868Yq5(UQH_>EEnGZ2rxAnE=tHEJgJ_vCNVaNSzs{TZs$ts;p88bo*f{X zBwcZM8It8@a>8y$N7>dTc3;jTXk&>rWk5k8Y%*Mf?Amv;>9Jn!hh+NJg9KTZ;UYdWJO|&OR0F=BPSMP=&y~!@|$7YF?>|1S9SU zI+GIGl}V{sNgcO}K3fgTqGFG&moLBLYNG8LyQMkAoedM;PuelWk;MZ0Bps%MJk^2o zg%KUlcm0DjYFip}btc_FLLe^Vcx|^u#T-5=2JvfS!PVD0Q_qmgH3D$Q;L|rpGJ*;* zrejp22N{~hdaSrLMQmnZ2m*W=|R362bbgGRC@_&6vy z@m*pL?k^9TfULu}E&``WIB_(NJJ_B1fbePgJ^@`vSSflzue{+*Xddvs#4yRDNh{sA zjtgVx7j_i?f3>=rL;-U#P6o z!raG&=Wy!w)|(tljPwpzX>Z#^{%z#7FUA{@TxXQWmk z+~nMgY*nY;Pd~9+VF*5vVdW%rF%yoxQ(noh@jm|chKYy<`Q6Ekc`cc)aj!S#`og@W zAsFuDjQWQt`oW1CO5}y+p_IzaMV9g2d-5#rV+7~NIn)_@9X(ySJQp-} zEo&9jk($*rM}`f-GoA>g=}D6m+ag-Z$%}gYaV~{M_75FcUh8Zd%rgP&Yw=z#^bcmZ zE(?WeI<8On>%Gr(?lJK^UTUR`crW^<02+t+cCkH0A?4$<9!{2>l`Z@kHASV@KvY7h z?(ap*3_}%G*c|IkU+#4`V1&7xOcDjYRV~WcTZsnxtg7-@!4Z>rb=+IRVQqW(?h{7S zK_PcfiZbh9@PmVc&lv?D9w`dOMBUACXme5*QnHwqCm;5Cn15ysGdn*_)?lHmGLw0S zp8OPR5jfOM=kQ+tp}PEZ_2kxE^mmiBkcC;Sp_K1M3#Y3^+eeKFNqxOi9oy4envL~7 zT~4pIXDKo3KduLDFnkf4dU4~a)jq< zUq|@{rq}zCX6f2^;6=zCJ^4&f_QG~XEZimK&Y&wPzh#UYde68Z=HX?)j!$Rg4fhQo z>CTI|7#zJ4dm9XQ>cl5<_F6cuqrU;A$h0t`ZrV_1QbsWEM?~?HeN~xSSp+XvaXF*I zexQM>lW^DTA5D);d3%vZMMiUxOGNnE<3Dl^2$KfTVBHmQTBl#=KrPD6u@@J|kLQ_w zTJ|h-%jI2&{p`F5;SeV*oDIOek=rzv6mNo4TV~kcb$P7QTR1px{qZk>)4KtAOREF> zvmJi=cc zX|E3R9KVjkn%++405(-)OVBA;-Q3mD-Nn@06#(iufVvB&8s=_*d*H?bo2I#!8-PvT z9;7I7^;PoftNeXrHfb+6Sq-#Y_eQ{pFM$}-|+x`9_ZCU%>p(V zM+di`dNSMq?n~KEsgk*wwXuYw7vLT{NC@NsaBi2&&G=LCIh|AAi`jfUZw+H$D*#U95lz_;7|c@+`N)N9>^*E z`Qbls0_6YY1iYNTPZ>9aS+M%QnY}h;z+cgK3Fdd>ToA@N{)!DauK4)BqL1f>_27VH z1CF0l@W+_`)GhB#ZXwZf{?`g!o60|R%XgDo$Q9&-=<)wIfBdOi{+rxF#v|w7G0PvW zE%2tq#&L~W{wuLvu`ma?RseGuI1E9X4ZJe|;AK(=fTs=g1su`8g)s+YQUja%w|M%; z<*(#@InBSDg-p>L|CO|^#S7%t14H}WIApTo{HwBq^Vi7#wd?@iQ~+>7g5vzI<+%px zkNw_M0dPXD5$9h~%^&-{DbsO6at-HSam^q5y(!gkLXr*FUy;op`@JdGab9Da{|efx zy}$`hY2fV#4n@#T15*G1yilN@E;l0>hWc-z%y})8|0}EiEyKNKjx)e?V1SzY;lks5dOZH8}r8_E$^5 z^{arq#Pr)NaYAxASpDBddu^5=mf&*uzZ-{4P+Wh-2wZ=fpl&JyxF8vU>#rE$kNw_M z2XH|$0{34r!XNv+sSx0TWCXCKe_OgT?O z_rKQY+GPH*-FME&V|Na{uBy@X~-H4;(E3?*C;mAsG<-;XiTmivceY{vLpk z49NXg#XAH8UV{1EI3xq|{1qc`gER7fAO1ORs)@KE83AnRPv#2TbOCz=5A}Br;J!w1 z|3&Cm9KijH$}chfHcQ-)d<|Crx6xh;TgW8<5C3=Lki5n7R~*3eryOuodB_dP0X%=j z0e|fGrt**naxa1{{o7<-tr5?!y$GHnbpUw8H$(vBCIo-#PptpigqP5MUnIy)$n*cY z2`^FnZv5IN{Ht<+=hv3_*GiD%rb>_paub3r{mESY+ATa_2KWsQ59B7~`KkUVX2G0# zxwmiLCp?gwkms**2gD`>kLUNW;ep(Qynj{c^Zp9We_w)|DnTB|P00IKoBxmf-c$+l zLMCF~zuNqN?DwWhkQZ|E^ZwN${;6Nin+hJ@YwP=8F?o%_|Jd(M1rIM|(1I=f!Cv6~ zMMvOJR0n_~1N7yx{s7M#=qK<_{C)dDG9cL0pSb*)`Y&6}mw10S3(0`Ie^n_!Y`;rr zzZ-{SK)%1CSH52(|JUuuc~f1;3keEr=}+eBSK$L5>hEmA3kiq!zX<&*#qs{4@=Hv= z%@QvpUxU^EZM4^d7;*`2W^G;wM&SF&TYqv5bW|8wKN(ghGaZ-C1?JplN^0Mt6Y6khh)0N@q|=qL;R5a9Cc0{~aFpqd(7 zgaN?p4$3cJLIQx1{?2jikQ@j6FUr1JSKu$QzMPWZ=7AlOk-_T!HV;ycrtbEjXBePA zr!aP~1TdI8u-w)7`FAP+aAbhXN$}Wi7(e7bdZsg_pm#E$w?RKs+%;Veko1y`Aay~o zF5iY+)8zt5!4Y{)%FPJ4ycNGD1!22<;BZX}!gdvfklsMpuAT%yNo6l>yAZ(X6lGmglY*#@KX#s@o>ZTM@3c_}EaSAB~VY|9Vg_MG@U0$nR z^9I6p#axiOAZ(ZSDUiCL>mR`770xv&2;0>Tgs=d*1^nk~2~rBec6HMPDFtD>x_E+= zg0NlQLtXO*!gfWmkh&mjmzNLMbV1lI?>nwZLD;V70Kx*OMf?wELrOu|z&-A3F$rpc z11{Uxkaj`04gc^9qy-SRtA;wH6ol=vzkbaE2-{V{fYb$HyKF07(*>||b(g0NlD z8H5E;JNTcTDx?&I?XtxRX@MJr?J8SA>VmLwU*;)DDG1wTEAN^G5VkA!gwzFLyGnnM zQV_PwZp}3dAZ(WnqH9tRwktY@umEavUG=CSbwPcGf3hs3E(pbCW8#`F2*qW8=bH2< zir=_|2ZZ7(ZA01xp}0J8U9$i}ae2tQCIz9m${!FGKz)LL4xf-x5VotcD5MmG?doU> zDFtD>JgGu@1D!VhsV^XPLD;~@rE5_QI!ysCPej)&fUsR9Ge`>{Y?lSgHC+(4%eo6v z3Mvx;m!;k{DG1wDc7(7CIxYNDf}rb9IQp{MLSwG~2vdMf+RSTBn*2!{}-%*#KA1%e2W56A<0ulp~5GRwgOdcF$zH?x~fNM4%K zAJ~1Le{E`FYY=RD`g-(^zS-`D;7%|v%_NRqz>-2B8V8mVl8=g6=G@eF!GN03n^-A5Ee!s?SYd~r9l=jwG z@TdE(tcGM$-k+PMZ`I91Td`)nHEFGBjeliw>nq-^u8yG9RJ;TNv3ZQ=zC7()^2{= zULHV_l;@V5!#YaBj_xi0r^q^4qRz&CTU9Gwgp}>yUN^thTjg)6ka=7H$Kdv2GsnFA zw0N+184klwN3~;#*Fj*n^V{n|mIQHLB#Offd{|?g$*6)M`ELs56ICjap`vZRHXr53 zO0XDTONC9>_B~sc32`E)oM3HA&6avs*DfbZDu--|YN9F7!T_Tj!gYT%Fkwe0WXq1M zO20!8HdGZlMJ~sf9looLFC9_CSb#YQ&k0T+Zdx}wVjQ6w-l+_W)D~J%rp!VIx)ir; zEc~KVcum&ryXfn#wgTa=@e9;0Fn$KOh5niRQ{U_4efw}HX>9OqYfh;7>w+*Q?$dVlL^uIBMNe*R58!K&wuQdNx$GguH8o3 z;Rm??O7Q3u@|M{h{A3P{Vu zvMGhXgsLj6wnFO!uUqt+yKtfRts438zo;i&HEn8p*eYOn;asK`<%ilbC&3pM8bNTX z7EhVn`$@+VE_@)pGNzg$bWbYCWCg&8R2^r z68x6J`eA;he)nU9^+UHRTVF^_9J$z~4KRcl^DpUl%2poWz3a~tO*X!r>HR#Q*_YHV zl?jtKG`Sfo8+5uv#lkekLT4rtC zlsQ+;Dc#49ts}nGz~5QhIeA{{24_f;S_@yj>hdL&-SB==H6NW!j`W*3^Xz6x?;qZz zrsVHt?3A}&l7@{?t2;{yy$#)mA8jt>%q|)XPO0otV{Xf2k%LP(lH`G}JRmfSR$DQY z^9x$kz^5m8Dq{YLxyXtvUy$sUM~iX|qQ{d>QAuUv^7~a7uNL5(3e0BWiPzT{yor;a z5iD5f=l3!YQ7^912#$yHEQlv3AS@CtDTi+ui#A5k9}<;?9=6%)*b2L-vhK}Dk?xkS zER7MTM-p$!=x;?k(=o}}J{NJn_a$zOx}^0R5?kS5Al4hcWqv2-!>0#nW7DcJ++RZ; zEC{=V`}68Gt`9|}zwJ1gXX1*YsRtYi#{ptWol&29l9Ez~Tch5~Maad(c&SAgST2;U zP#6bDrC`~YXe2mF3N%ASyThrmtlr%c>Zc!BJ_kcqg|O{#jP^!aapc(;^)rob4Hp(0&Hr;3s zx0j)c11&}(*^2W>Xww@B5kPKYWv^&21}+}^xGDwOsQw#jH@v2x^Xxel!>;dmr*J=% zolss%C=xPgBB8apkdDh3GtIpY{U%*`T$3&#kfz`%KV3*Rk~G3O!Wb+6AQ;_^EywRz zfB2B~8>Y#`O@*d-0~MaVWQw5A<%$y4)-nSRX-_@ksdC@SD9Q=4F;cbnbjoGu zngMm?E2I?Wq`uO|?^R!9l1knFk>ML^%A+Al;VLSSfL@HV)0{k5z_hOAgRu3)acCQ^ z+z7WUNP_{p3o*7$$hh7q#VIWT(*S!8O9XQe(Q;Qg5+2B~8l?uLi^H;vwCzCXQ0Y)9 z%ovFtktGhc$gl`5y0`DS1=wQWlG>uNFYt)=h(B__kK?jdTSanoRQGJ9u}Ybcb~9|+ zDkEL#LhK^oObBHad6~iG_N+M1@P-`0KvKmR>Ql*HFFsnC#1HR>?GI4NZpTp9n;|n7 z!HnR~Q;Gztr5rP7<6kPNrFbKtm7ktcusQG#F-;+(!LKKPRG#Dv%;;=yh+$zezYX&s`uuYgUp zs92iC?X57lN1=Hi#MxFlSIp8APw{4w^lqg+{Tg60hCrh6(Ii@MNM#mUu;+AzyJQ%K zfq(l^^wZ%3b8ENf=`V(P*KvKcw-2Rl)^IR<t?I=La|Ny=?%Uu2hJDZc=E*pheOLwbBtMU8Vbi4H zJi)gVSkmN3d1`ECOr4^mHr&Ky^FeuKHWSo)o!*W~_n*i}ia!z1QW`@fj)f}Cj5*D< zGAuRdUmmRyYBDGuca;+UDzrZw;Aa3MxbsLPF&OSAepV*viE(2Qt1LUE%!nIC1j@{OikA>grl8BJB_o=HPTVvB)ra%Nw%fZrCx9W{Qxa19)3!hu z!M+yvnsoCxE0{@WEtFLO&aEsb2D;c`hCuP;SUypfBa6DFAt47r2GX81`E~f0xBV7d zPR^m$8Sd43eb-H(4MzJe)-B)Ej}Q7Y_Ie!}c571nS~xel@=mMv{F5_uRlbBIwRL#e ztma#0Z+2byHhJh}-GWEq(FIpeIk%QK%e2bJqq2x%qwkL1bJKFgM`xj55SVXnO>i*O zVv|8{j#0KuogJY60`G~>SDSDgsQA6cF2y{D*|zn`fW%Je)9XE}5h6VvLDLzXal~UN&n#<+>^xRH%ilZH2C99SE_YZ7b#gd*IM&>~ z?BX@L_2B-3^WxI#W5@ZphSrJ~jtB)Tv;O{L4-M2Z6p=F$h}Thu!riKca@nSH+NUuj zlzA3#@{)=S&@EF(jhzJQ6P}{g*wDRhwce++j*gm8yGuM^qV`JMrjShxBW$}82I?(u zKNdCS5olF#&8K9)yqvXYAd$7`F&$?{Pa^4F4)Om%wT72+H_?mJYdku!y?d{1F;6tU zAs542yoKIqI_lul;j1Qt=#RFy8YtgA*PXSf6pVSJsz(eL+J3^pIC|@?57f#+thRvx zpH2%FNDhVUiOM zBSQ}gfpdom!#fyXXu@P`QOq4U=nrCXM82y_q-7T0bX|Sn_zo#0PlKq$z04VjrDZCV z3}fW^vT*V6q@ujP)2iyloj zsN47WqnwwlMhO<;p0M?iaFx`}W5#(wp)!VwKkd9#pK%l&^+Z~F@Cg-(l>K;_p2wJO zvM8pv@w;SWkWa&528WgB2XMX!@!EIqO6#J@lEQK0soqsX|6Ed{{nlUl$Eo+}VXb7p zNZ8$df|uX%&^w>Ke*;S<XvVOR;Fwz?AdK>CPm2A%9% z@P1IK%;IrxJI?evtU`GnhW?CHA6WIK%uVd#k$I#1IV#b7W#u_7l&N&|}#w zS>?~AJ9lJXu%$c0aXxOPaVbH59`!v;Mp`1$l$tuo=sS(F#0lJ z95l5TFVa9nNUA4}UWWxtL&(&3a);>|miEn}bZZAQhyDK3kfE8D)2hy@`9!2|yavarf#Q3na1Z`9PygQW%HUe1Dv{6xn(N?kxnM;J>yK8}+er(%Ci9N{t zAy|(v=&0GT@ccksui)IF!o z_@achvcl@`v#t2~NybC1$rNsz#RORAAC6ILjGyW?sOpm}#=>Rlxu06!6RiK3q%Ojz zk*Z8vBQu5~lBukg+j1w7(b6JeQnHDWa&=i0%@V|NdSouN?`=1_4ToFAu$D!mg7I7?DV59D=fEn5JWtgP@4Iu?DfQ? zg9R-TsX9!~#Df_-ZtQ*g%Q_4w4KJHfL`5~iIdaM@y87z7~{}QZkC758Y^ZyOg{6 zyT)~yRMI+H2&}^lvtRqJ= ze)z7VI48HYj_5F_{nqpvCNQiv8Whq}Dnv_Bk-H}cI!%!;#+Aj<%|gRkMI_^wFiX<` zi5+DhGuV1`G#69h2Q-U*-W~ylH#6?T-~6Sv!DM; znnifCftYE{Rh>DyYiDRmr$}*L^P4U#) z!|$2$CAJGF$HP#toWsvS$E`SsV7JANk{%$fa_S-jX~S(eT+{@~D3*bX&ePI~YOYwp za9poooU7aNU!tKhwS|NM&)-T@2`8jeF2*+WN{-V%@>lO@V5!dgw%pdgY=u80+q+#{ zf|QLyFzIAc%~oAUnJR8=;p!kcN!|wQi&!ZX7D~q@Xo_9#n&}*2<=8bCY1Ad1j`cjZ zTs48{3(qS5fOVHm%)9l+*lC?U+>ru-c~Na)6K>e$IFd?e`E+BHX4u%%1PXG5;j0DT zajh+UOrLP%MwXlbZ5*@kFRO_~b21Eio}pF+c6E$9-=KY~?=z<17YhmzO02G$Vt6|_@v${dsj~MM z(l~u4Td`AT`}}9~d+9-C%jGWgR6sT9;k}3HIz)F=zZo%`CDA8YI=v1+>5QQN6feEk zm00Rq#-lJiRyV2oCEon5-yjqR%rzRfP|DVcINIXw037L zsujfZI91gOM$DRO2Oj0t+l`A%sEnx4aig(KuZ*F?bO~P=O@xWJ@9GS?pxOdS+nl1( zI^aCJ$;|mYJiA$hzUaTHOBUr}isvLbF8m+lkY94h&syx>WV8-+@T!bqf%zrf*ehNtnrA){@scMw0FW zqX)F>tRj0TyS-}X9t5CcO<@$|hrY>=vHhf9k9M;AvItq$vOvEcq@t7dF0xx^G4R;p1+ z7duotGwMBdtIw2~(?DLUZW;6zH8O{DYXsk}Wy4lUfe1GAt^(==C`nYQaQy&A*lCIB zCtT!B8|1!b0H0~HbQ6FNs2TxcHu;bh5k{~!k!qkyt+96D!VDADvUiHPwP`9+elEh; z9AHA!Qq+-<;MVeiQ(s3e9Kn-&$|P2(T}9H2Gdb~fgW7BR;~`_ht1;-$bF-dGo1)s@b@2)AH$+j`QbYr;qxiB?~+ck zb+4d=viO^zB_IWA252!LSa@U&eo*Rv&F9OVs z4b)!3(MPbA`|(JuyZb@j3aw9Bj&U2{>18cp**cD7PAw6ZSteE=8`WQo7^MVpCyYw%`8NaDKVzqDb2=YjHp*gKh@&Ue{UoZDVe(i=? zji9bho&#W4m54evO}pcD4z)se-9g`jW+u9>qNqc~annMN<=^rzdF6 z_e_cOF-(1}WF)5s)oe!qi6|pScAk0Y)U$S+VAJ~9sj(F)=`x=D94~o4LCOYhBZnY| zw(`EQQ-?<;x*L9*5#|(_Cifne#&`OCn4cQDMgH;A_+y*z?Dsx>P85a89AWo3-#buP zLsgl%EBLh8bOA?Y1=Ue5dC%>3==xO0`w6vvkv(pHZkrX6Jx_e@v2C2N#7bEui<7ph zL^QPe*}}MG@rX0B-30gZ!Cg12r$po-ZcuRB25@W?8`K8m(U>Zg;l6Qcl^p7=9uca^ckS=ki!$(=&0a6{T~guVN#xcVJSEoH#p%Q=^FrMy4LZErdPem^SMsDX*84hv!A5Hjv+ ziHfp~vem(g!e^?fs(t5{xOSRvJnvsaeV@kk1q%nMN$JPBGN|_@($pyrS-gxteBq8+ zK#r+DdyUd3L%TFUeNPK!j4rBZjPAMf{&dB-i33iSx=BQ)C0E=RWil;}@5ZZ`sf>t3 zQmNXVdk3dnOYt8cDhZIc7Urdpf2P^JrHjUbCPAetnDRs>JyQgCDn1Uc7LNsuuN5Dg zn#G1>-MHH%PY5QfmRPG&zIdcgCnqi?Q|GBPt^BGV&%csM<*v5nh7GVMKiTAUmuFQza}!!Mu_L+(EUU`9PIl|60q zB5WRiI?(|2jOIc6V}b6u;C;u57(^u_2`hL`36yZHDnN2;)KJ(Cv(F2j5M3kL&jHLK z$|0fwD5Qw93sRrOkA=;GNk$ENy6Q0AApRg-4ZtwIb@ps~D9eH!mLIYGGt)zamzWZS z;>Rp2;)KQ`D^xL5XYj~?pel?NO6CxTHY#*e)dT?;zkG}~6ZG%mSZ&x5l$#ixhzNi) z`bU)%Z5#~ZI_N(%ij4u`!A7^*kQpTT2;XU31Sp_~w}^#E@|OjPBeDe`hqN29x7m~^ z`k^@!235g_QIVqGMU(}c3C4t=V8#U*Bg#s;f5D9j5@si#Dcd zb7mJf#EV&Tf{qdA!(x@wq9X-e zf+>@)cC2NU^}-3?Sd1D&sDi4HYQ(CLXhf=z@#54^@}Zbo&7?;R_?SQ3v&|>OvsEVKZGe)F4{zJI1@cCs#ipTD zAA+yJ_b}%`|G(n%N9_m;JR#FYC~aF+c#FY^^MT>uazf<;?pjeepe>&IM2>h#d=Bsr*2Cyu9Q6S?n0vt;isJUXq}#po(NMx(Z?OP8Go- z@UzS@!bJFvIl5us55x;Z1gvJ{xAIN!m6X1CAZ@BM98!s6cv9sbSm*tVh6_X?j%~Gw z^Fh>z7w;CQcLYPWTU-(6L)SDaTON>-htC%qgne6NXv0{L4xw=If;Nyi#v+wG2A)AJ z((fpI!PsF8&uyi^@<*>Miff%;;0`f#^1?QdJ*It==!4W8BuXvRxj4VbhWIv&7wa5m zPQoj*{bAemGlE6bH^Dn3UIdE`i1BTnJLGNKi+pVq3w|M%{6Yv6MgAD_e1Yt66qIKa zIp-H|l`p+$xX>wnc;x7IYAHIho_kJ`bKV>8cG`Urs4SW|yk6?Fet>Zit9+5L zae(o#a8z_b;72gIzz>*;|NHF^iuHqK{bGtWfgi^YC3DVKFKBblPcJ?nTnw*YY`L92 z^c4N^wo>`R)Bj2+T0Uxc{7|eIr;&$0l1IKWzlc0nhoUr|1i1{>?6nqs-)vzS&I?^P z!fyL1`_wz)384u9M3qVZmy4WrO7Py!1PU6+q2~ zsgh7rt!Q{@D;ddqdr&kc%h_r6E*ycB2~BFYTY~Q$X2WLpui@InQ(pykvdq|tf()o% zWykl7g&!q8n*-ofZA_-4PQwN{6WHOf*1HueyZ3VBghe{OF4KC=Gw_OoR`4o0Yc-LsSC&qxQXhc>^ZAx#Ox7MEumXx?ZS#A+UX;EYj9R7)|MSC`I-( zDH&_;_=B0>i-XQ@=$jY9w@!oI2vIxQPPe(z6^k6Ox1Yms8&?s=D#T^7f9|)Sm6h*k zcbAy4V=|3;%9McHE}j999mg429xmRFsH&dqL@eTng!jnM=5Y}yY20u zS{ykj>#De0W;Ax~O7C)_nI%Pv*AnG_r0E>^;1XF6`ajjn)6jI%-Zm%smUO#@T&Q|? zFS08Is9$k_L?xmbE2s@OKK@m~q|^*HP{xef|73}er-_bL-p6F*PK?CSJM3*>RueWO zUfcv1@m7m?PxL`}Mdrt%^QGWN<4fG_6=>%^{lmI^;kOIl;4>2B!Ns_25ud+IoukVV z*453lUi~_@&YBfV-yE}HPNV@9j{8~JkJytlL@Xc${tTZPDSZMJX7=D#T zZOd6ZQLBrd?}^*VAKiKyd86S`Ym^K4zkCxo^RFD-mS+9n{UeAU;5jNQKtB;tp5^6H z>kLhzL!VD485>i9`6`4aZZH%ohu95!blou{DZX3E$pl%kpw~4`UJ?grPh9>f&&LUUKwkXgc zzs#1Ao4jpbbIG#PF+u4R&ZrT<>j{A1bHlGS%JBisVs<7?ToOxNR6YKj5Fz1r{7H3t6s z`#Fayq`qbv`?pp)UX67>LE#6=3_2-m61JyrMb;i39tW}~MrP~;;>w^}l=c#{6C&q8 zy(MHgxD&b>F}cz!BTe)u12e2nt&0{_SPxsU>%5v|Gi7|7;|pPSfA8AL+GfgLq7EZU zjOghdi!|?K?_gb0^DKwgGxUlcEGYzntFJz!SGw4-H}E?g3ES`xDp^FA&`wYo$FztP z2#HOA?r_bF(t1~Lg}<*;Cnyng9r6hvgnK_E7{@2VcE4T;OdWQ$F7}J^E(%rUc5|TJ*au z-q?G-ZGgk)X6>H2yOtjc(IN`-6z&9rmK79mZ+T8 zDxXD{lbJ9UBBS(qTxu$=lj+OmVNqz&1Eu&Zf5%6x_^dn^`na1VH7$i$b&pyZtcHm* z2V~)fDG{F-!P{^|>n+|A4tQ`+;YByvWrUtlPPPJV%FX&?orue&Z$6ulJ3X z`dGV6!W`7Gcx{(oCc4<$dgXbPrf?8(XOevqGbermZJwe_rkFj^{$*%3)uAS%c8CaxUq-(nk@iT|(tF)iwcnLc&m*Toa zGr$ z6{>jD(NgAv6<%k+yd7oUVIE4`eX++2$VnNZMLX$$Wv)RoaoTP-{g*je)=c5`bEPg* zE??jIFmLFNk`%o?bvtE_f7-)jMwES2R#t#t+WzQMmCiuP+LCVBMx(jlX%fEejQmLZ zge_jLts=q`V9fMHfL-m%r#(IIXy$hp)#&?9WUJyBDo3jE&)bFWNgf580d#tEBKRBI zYrYq|=~gwcZaf_zPAo_-U*WQnQW}kTchMnp|807R9h@+lMOyRQvH3=dt-_W=;!!&$ zl1UW(@nPtltXk&JjuFbz?ff}fqN7gt@BFYRC1bJdRxd3f?#ISYFtm%B-F!BuT5KMg zV+?gj%txk&W>aVV)_Jb>Agm%MtJv}D{;~bRfa?SL8Mp7oPwx3`tP>0-YQ5Y_rN)?@ z2IfymUz}_1?Pc_GKJ-05id*LU;oM`bT$jbKDkZXE%pa_UO;Z~o=IaOT%n8gI&e@ZH z2^Btx2}oe$lx50tjv{sHi*cWsad@RPlOyqUQD*t9$g8t7NIW?b(Jq$(iG41JB7i$`@g9QOHAz(d?Fk-Ri~$En(cl ziq1wNd&K+FuQNI|5#-aif@V*ej_KbbT&#Rs$UVc)*vI&qWTttSuR-HM#(NgWt!M!o zCrdHBR{_t%U`n_#c08a9IUFiH-n+1QR=P(!@W%#>E!d2!uX$7#&9L%Vc=8Tx#0bC7 zh<&{p)9bT0E#K+n5*!KJ$No`dHNYJ&geu>u;obc*C<{Rn-`7ZqsH-B`#r+n!)M@+4 z?VCxBy$AAZ-p@4`>7H~d5l_h5~#vpBS;-@Tj z=`cZGs@$h&f9I%%&&ZUjv9QZU@AKyhO(vcZ$O#okGx>mmLjx+rk)~oQo_#}kxQYrP z9B$UluHTQH<|V_On)Zk&OKAtQ%ET;C!xiI;QRqzcTsPSnj;1}+Cj{Rv9KRpwJQ*2? zs_`zq_rZO#s`~XfI^sZnJV4O++!*C5nc7D1GgbDBaiz5JGP2jxRl}wAl-?W7bOL6b z`nHTI@C9c-2%G|`c5_4qZ>hX}Y0Dc1?1{c&Q|M8FzCv?hrEi!jT;CeGFy%fHky!Jk zPIZr`G=HYR1R1DoIeY39ZuLkiC{&K1uB9ArLrmmDOi)6ACVFZU6=sHW?+u*d*>K17 z5{gw7rUvZVqQ187EJ#pfv!*KVAeM>7!D<;Au(yrsK9tZ3!@fsA=N>q*1 z^i*_O7v12iBStpfuCQ-P?RWx}ZM@a3<#0=ug7MyDc?{l_IR zZFdTUov39}zfCL0&27ll9S|8Ti=?PRbCxaQn1996bzRzIlo$|{6y52$`U25Vu!;8+ zjv4bwh*GXIa|8MAsBa0LU*M6K(RsQ^p;L4@&W?Ed&Z_3erO}aFy-Il!P^%OJz%|XM zN5)#-9%D`CmNLaEa7apV%y z6o<+FFxrm8ezp9Du|v`E=f<%`OP$*jJ={J1f-Qru`O`YBR8#@HMWfYhatLdRM932R zx?%|{XNC9e)YN3;drxBa&4+|e4IShXmsSL`d+gEJ;eMN@A6n34ZdjVmrpb(E56#^> z71try_mK0jvyeewE7-mg6S<-p#+oG>`841(m|l+LyOGW+4Fb)nDA+DOq7f%7lsUM^ zfeiK-{Vz{sn*Hy^Sm=VDbLUpw9>o;NxHtmCus2%f--oGfB;qH~f3%~$r1MuhZlNn* za1r~VF}Jzuy*j2&;5X`1NoUFRV{sq#JNoF%IJXyUtnzT#MU`Z~3EF#;`JP6ES@f$P zk6)2ikqVo~CbLn4xz2(Mn6Wu^kT^-OKh#~@*Vf+X)!MTzs>s7#$W!$z<`A~^#74YP zT^2M`)oICoCGl0|&E!LxRl{gfJH!{yR(VppX66r*KGTjg)e0Hg_Ul4wmB9US zY!o;&{Par&U-kI4cR<5|-*(doJ%E0DFhr+uANQVIY6Ug(e#k|jmGxHZ;&6*%U{sR; zb?|h_X6tFBX`Ud$_x+g{o1Cs@rDjp`ggJ@|8MQDDuTTzz?OO~OTS4d$lbfi3`m#hU zht8<2fpFm#_KHA)52X{5X&nKIJs1+wN`+R$n4?%(@L7@wFJQs?vk6j3 z&SR131t(JQfBTa2x{Qt#g^H^;4A;s>ep9lN_vs32fxtMDvQ$&;ZjKR^Df|)5FBOk6 zyozf5b0gitvPhwbyn!`0?(V}h#pL#+NjSggJe$Kq8EO5m85 zM;Alhd-K5sxSyg@aAAo*VbynbN6Oj*l0CNOKi+&$l??roy{|)a;rC5v*e-N`wDHJy zGBVlZsBZrB{Yl00;TkV~28XFfrLGr5b0z;x;E{i0mK2#HO$K*N<$M3VA-H%*%}>)q z(ubds5i%ZJyv-9a5bLNvn0s#!;T$YagopmHPvuEGv?XWJSGWH3*+B3(c2s2ne?u55RR94Q7oe zDbkHW*jFzv&L_EQ-$uqv`f^wP_)c+!GlJXjxiu)Q$sw{(i9#Ct<{vY!|!PgwWpg!Ca{U91Zg{c?IQqxap^4LtbrslHYD8GK!;=o zJz1U}kanB$Bf6t|?qGdQHCdJt%?bX{x73)H5DG+HVI!x#s~c@B3D70{9xMa2%o`8K zOR*85&taD|=ZwnD9u^s3Sxs<5i$qI{={gjx^%lS;pb~U-FXLIFHx!o!tzjbw+TCgc zB2O82eLD0;;7BS-XtI0q)TnsG9|Y0u{SbIR5g_uXwm(54uQm70;|>)L^Ae+ZR0p{D zI%;Ulcd`~V#i43zJvoDqMQ~cUXofa+s8@)u*rDpq9qh%+kcR87Cy(grY6(8ijB1c_ z91&ChgiEqyRIeqw9lU8Ht+TU>4DeVH?8lo>Ute&EE%Vq3Jo^#iv+Dj>pjCZGWqtCi zFxG8rvaOj{r4hsSx%oM8)+)+8vD|1WVymuXE!t= zEo^VdjnR1`vs;JGUgsML#E1u8U@%1RKJsdzg(ga#7U)fVa z2Hc}lTaju8*t`)vI;Sp^3L3U{5?-V}jtu`fZg1{&9R8{Ta)SY7)mxTufj_?CJ){UI zR-f-c1pXAW@XWmc?lggo=an}y=Bi&eD~>Z(C<^l-hsp%5iK$XU;elFKd&|Tdb}k?m(9~KmL89M((GaPNX|NsfWNPY3A>~F{Z*g}F z*OJ^1X{fB(I{wzaBA$sxI1SY8ts;qm;P=Ll*bmi@9sSzTCx>#*jZ#ItzagI{@BLfE z8dFGdw1fBn#EYMDsEoevZmembQSaEhzsvm}knVbdJToG}?wl1YW!PyfXVWuJ8g038mqS8bbt1Heq}gKx5eb%h=BWbR=uXo@3RIPeK6}0*nD(pQC2SiM^uyl86%dV9C()nyBh=+E z$NACLDbY+}_TA5qQYj9Nrx-uE=5R@nG(ei#my_5{Wj6r$wDwzAow`4b z^yjIm)f8G!R%UUM52@Ijcg$-P;inoVOm*~#ADWF2ffVG8RON$=N0j7DqMhgu!~}(t zW{cU!ufnrUpOY`~0~;-jhFt@h=xkbhxcNIu;7>GNj|Dl_%CWBbSnABbg`YcC>B7VX z4zF!1l<@VMvCj`unJa!pTo8EMA}q+W5VLHvx4rd*M<1Td^BnW5^zP_}hxA!IEK+YP zZj6+`?Y?E}W`#Z+yG_D9$~7SxO_PH0>Emrgr7X%=h9uD2?`pFMGu}XTBfZADWE1rk z)z1(B^##yzEd9XgQU-N9Fn5+>cawkeiZ0B$(6C?%dmeRft2&Vbua230znO@>!$TuX zIj0`{3)*Bze3ruNC1@I3;QC2_@K(RMSw=bX$~yn)vVnSglTfp%ryEZw$QKlvnr8n& zY~}Sb!=&Z>_x)jVAB&R4Wr0%AuTPsj5ayz*ACK$icaN3r37t-sW#23` z+wX)(GB43^`@8g{)W5;+&oQ}$^EnoTuRyvOgBhKph2J1gEOJ2vKN*+7xU)RWL#GWJ zmB>&C>*8?WE!kA}VN*7x0cxA&CRz3iU7n5!d+$Y??T$cCaM0t6TJw;?HRVWdu1 z&T&ggc$UvBcJ+Nv7@e2rR-#_#Gam-AQTB7brOQ zxtN%tjOc=TM%VfEV4v@VZ+{67y5`Moeyd(XXWiSF6)ocI)+gw^Vpkq0@g%!8Urtgt z8X76B)+z6PjIl0-;79gmHiOhgXhn2mu}RqM%~@FZRBQ+-OsB|XI}F zb5c=7%*PY>0dIYI1gW_TX|k4tUfu1g#>y>MJ(>M+xOylT)6Zm$0;#mkw zH^K(1k{iwg`|pPp&@{xSAwj92YoqTH;#`xx_(mRK)lw2!LB}G@kn(5(BHj1XBPcum zw4KJ%!}Q?CwC;ppbZRhngW*Dlan%_<8;*|-C#>%7d=&FB8^^lbDhO}|qR68`854Kj zit;L;-Wn4ZlEcMgawX>6#b-GIq$v9dbZNtahY94FNaQdRV%w(c=bO8k))>cZV$O{( zXJ!w#fKIfxxIQeKBRedIB^P(ixR<94YkDn1*uosCJ?r@^nAZnczNBzp_G*H1ATk+{ z!Cf^oxmR@MnS7$ z`8dbp@v8}8(*1C>4-H8Hk=$aoPNQYrPm??Cp^s~8>_V?jl30a~1 z6Zp^nBTs1$F@Wdr+Xt!ZgC-*}17T8aRw5T55uez6lL()E)Ky(?T5pKj*x{t;lgahb zF@*e5ntY9m%mRD`hMmjFzSBd1m?l{o$HXtjsolV}ahLl=DE5#~w}G%~ZLtb@JZ{RAZqy`RY-8l^Qi` zNVH(0af)nCL9SS`O-@5D+33uq@f1+9XiJwvjU$nfW`=P$BT-#^80Yp-K$DZuzdmf* z(2jk;v$Bj;wOApFWXRm1aT!0XibH;e%8Q~~J8GqO4lgO{3pAS*l)!X;2 z*i!JDM9PrZj^nGB#1%}0YeBjT`g2LP_<6cD1LWy!q#|ei3Pl=aQ_>%z`bM8PTSpJ= zUAA$!5+I4P=SeNK{k;Xqk6|kuh!+-Cwqosj+zhYu!#Mm@2W(c4Y*Np&c0wN5HktEr zMRw?ZJeV5i7caAw?$%a1_%zo~5EFpDr62ew9>7|r8TGj39zSqbb(w#qkBi|lxBc2Qh>F7#ZdFRRPhh!LR;n9m2f`~Q*HFo+#Id8vN2qZC%_lHw3j}RouEhb=KeyoWj)qdwpvU-$l?+N zH}d&2cxgBaw-%mR@i}CtH?wEw^Jj_2rD0sSQ@fR2x8#=&kOFvHkU^(mxC(_#R92@moc?viaNUMlLDQ))9Z6?9p6SC&A@6Hh75y zqdJ0sV^p_{57;{a;uAX+vSv3UlK4wm49?mcX1P&}JPY+c9-Y^y8?l{d{(oN=~Be{RL* zr#;5ziwlj4B_BR%bDc&X1E6)^}B+KtEb8&b=H%Ro@OF?yBzQyOh^&d ze2qW4LU)EO0U_QR)TR!*7t1yKD2WR;jltna6>|7tttIqw-}yVKQH%$)_n{4zF)+m_ zQgbWm?OZcD5Y4m3mQU7U`X~+SR-)U(_n2-T&@;f~aMEINnb)mlxN?<_Bqu3eo@)I1 z8FOR+J1q8tTL7vJIu?_XKm{>txxf;Xf*8kv`W~PHFVFq%6KRI=No!9NVyIDr<&VfZ zjPOe7q*;xas57;j!I7lYc*?_-2I$9=1)--bRxOpM&){npJHt{g3Sn1a+ zmCvCNXt-!B3PM;lr%wlEd5YVqhJvyj;>ZRdPmG+`$6_WgJnQ6 zC_m%JWfnUV+apF6bk6z%dCycAmb07(AXRmDz@DxU9gtNMj~17@r1CaYNYqXZbkOU79W+r7}_uhTP2E@A1qh)Y~hlWBh#yZ3YKK$ibWEX(f>!S^e%ZRo3sB zY%v-8G4d`%f~2Q^{i2}7p@p*V$0?FiFM_dVxNCWR5KR=RN|?Un@^oLk(buR@Gqzg8 zl=^VT3LQ6oVN|F2G@WHa=OMc2agwG=ZVFnAe4xJVr5{lD%U!WP>QPMP_>RBOJ)PGn zQjM#zSnQ~KGEFtJ#+2U%8ZUw^N(`TYRL#UMBiY!Ftx?d{JFp=xe)U?QX)XWy*JqLn zwiYvY5B_u6%m%m^Q=dDvjY5ok8)hD8rpzDlyUe2o^cKI~g~P=zE6;Av)+ls{j=$^9 zdb~|aCI2w1Fpt=`j=nebtQgBK$b;C@JMKMFg3VGlt9<#9-Q2?3tzBwb>SsBErv5nL z7#4h{tS_C^GO8@g;yHjP$}!PcH$2mqKfdSo<7mPW->8blsEW>`q`VV{{H%R$j6?ls z!q3WB$eJ0I!kRNQ4G5D@LnW08k6NCy(HI2BuQYEXf_2Ku*PSF*>Zu?7MsDe$-?Dz) zFPu6WDwaP8l9o%c-h_SV`)Si(j*yUsR$2Dt$dKqL)6b^ZP&cnB-#KJ)xalXpX2w}r z=+Lq0ZtOxm<6DfvMVr>UYt;b@1vmT4(K_#@%DK_`t8Nlj+SZU_hf}wNE#xj)pVFo4 zbc=yW15KCB1l%vOp@O4sNWdHfpdQ9t^c+sU;V5fdl2BrfSE$$1zHOmarB0y7>lB)i zsWB_SEfTb5+*9+sODVM*M+oEcA|!O1QTA9SM0KINrh zpVG4}e~OdWEy)N)>FIz`ka+U99!gbdS)3Y{Px1jQdA;Le^6o`&5qZJwuVX^KxNR>L zw^SEiQ!LPyjV96$mCgH`1}!C;mat40iOZoM8&@%3>uut{o2p_#K>LV0SIcq(rc66_ z>T7Xo$*z50n6A^Pg*e#zQc+ra$izb;?O~H?xsb~4lB&Svt!+>deEEGAZ?d)6O)68cbj6k*rBhP$&u6zdVL$@g!y4u3ca z%|028Y|L4hHWp=Hs_hj3<9Hf36KM#R&)Rng@K*(WYDIw47mtR<9{YP!V=dQ{DEdhz z6D-hQ6xky-)si=tdCxfZ0U6T9(^AZ%XtBaW*O$TUc^5;*l9y=b10Q2HciYU%+o;@5 z**4#UZ-CTAI-8sCb252|I%2M(xKBDBY94u_0V=YOjWgq8t92XOH&HYus?n%@8?soT zwumeE7{uMi3xB_lCaxe)W6w4kry`hQV}NnMc+)Ub;Bna?ztiK(VJ;XKo%S||gf+&gp1DCdq&l>mpQBVt(MKJmQIGf>+2iWuyb$n{ zyG0L~mC7m-Jvw$rdRE_kACi61b9Q!zyF5fYl=ABe%!1FmcG2Sfj%>fDeGD0Ll?sF! z#LSW3RZuuDsZ@WY0@x5M-fn=}CFE#SAPPL#t*|t-Ji6~ZfcCWG($PPk?svG>hL1Sy z?QgIv7j&b-Lp%rgU<8@-VctWbtl$t1N(*?Ud-~_+TYi=fid{TJQFZC)<27>`B3UPD zwO%|ZKtimRcQK*o3!9dSiQUXmgNj-{z1P=wcyp7C+kfQYVBo;Q{CxU+_vrDw{mySM zepcs|PH=~SGK7`%T5|>fXd(PA@u;+^*2T8N6mO*kpu54jJcwapcn>JfxPtd;vA4Kf z(|lU`!IS>DM$iuPutT|c-i~kx&8@AUzbS@P<_sIVF?QWy{2?p9-0Ezzqb%fTwuQBe z0>620hc??Cwa2(V@tMoN%Y^G;Fg7FgL&}FJ+bFR}9)+}7jmhlj*?cpx8;S zlk~%x8u2alGQ4rSi2tX|FKpPgD3GRX6I!-k_^pa9i<9JX*eQD&R<3aDO}o%_rNb>1 zbov!=;=iSSXA`qfsd|f14yzoy3LB0_rdMQh=dAR4AD!UScvE@_Exw={N3ES{MaVQ( zMHaC;wP3z02a8!??un+=Lh{wGSQ`4(wzS8IyPTlFIM=y5*hbxx>%+DLsiSURZ|3>2 z@)Sa@W0HNl#3IV(mEx`Ap=gm>LPfHo#2DwMhFW!O1IQz? z+Ai_OZXpTz!X$Vk@V(<~20(ek5P#_6%j{vWO{W6D`P=FB#SzhLDlC2t}r2 zzHmpdA^Qm~hm3`kh1!h#qGnQLm8D0|)1cJ2;j=pMg}DsizDwBk?bq;u-FC^SpA-8A zb;Ka$YdTH!9B)k3s^;euUWABV$^NC^Sd(Zo=b(}G>hX@qO?bEPTGEX@^!!a#=6$NM zoJgMjXDO7LDVu2hD5A9fO{Rls=KTS_%L|;1WVk8nSiJ(xHI~k&0Cq}aD@oMYA1G}m zkN&KfI#h_kmH;12@WXG&BWHlfJ_EH8X-Q`8L%xrT!e&C#L!vF^_)CUQ}+}O6RR0Uh&kHt``*`q`tbAvsumV@-hq<4=DkoCyfWv#+!vgT=hPrPnm2Vh zMKHphfFGdd-O8_`ar0(}#Xw{i1+OsE&os1Qu#8)=5<=i9{jLYT!l*@O;>Jft%O%q{c7)CwnkYo?12 zv04l)XoO+3QD19$q;Y4gIlcL;$hqh>)j>}HKKr}xbr^3jyTWx@;qq3aL*bC97#T0S z3d#T7h4cM*!i+5uvcN}i@jJ^N){Jk;pLh6~F#Fxe1Ys|(J|Vy1?dH3cfaY6}|3~M0 z%l3Rw=|u?n&EI#NVrH;i^hH*ryR3=Xvf5&oiev3h+BUOpa~0;M3;I+V8`od7E%C(f zw@rjCKg70<2KpWYbrX}}{*0F>denjp)7(zPJACQ6)NQs?YxTISCAqg7YV+<#sWSqOg4eK;oEcWq8E2Zeqk@+=v0VuP0!8sxwU#c?_()?+>tbkm$#N{M$lQu&K5XZ ziDrujr}`?05+xLE@oqDea}stCB>d19{T9a#L#7owe6aX@MggUuFtCsEEffhI#+tA^ z+ggG{uvGu_Q!trs7VvWbz()z;bAnvzSxL_nwDBIqiOPe2yA>xz^*|Jen?BjQl-HrW zE-P$a3eqa*m5Hx2BYh40=C-6+<7|%|?3};)1=n)QgY1QB=V?8XFG=TG_w)PKSZxeG z&LQhyLjNt3CKP%x84Yw(X>_H~q09aqC!ca>%k1^2P1vtll&`E@(oY6Cg@*P)0n0&p zMj955W-7RruSy|vMPZBMH&TjM$jFcekulLYhoT#h#qs$*WZ8Y&cl6b%u^P+K5594I zQ|1+8j73$ZRrUE0+A|jPNoM9rjoHb4o*A{VWZh2#5>CT8@>2%|T;Jdsg5=!BarW!* zV6FB#Sau^pKsI85caAE-!({GYxr)XRlQkc6(0noN@r58uy|8(yaa>MeA);5gS>fXO z$UR5pmA*>|PrTR79WB(8fTLEvhmZuvtmhLRV#BSY-vpf_>s6|#MJ)|)8DW7&IKMJ9 zh;jjuJJHNx9AUp=A%c+pN!w*{3D4faDQ6eXRf6N4osS|1G#hM^w&a<3IrPY_V0>^& zp)aup=q%3zR}E(Em*ZK&{jZj_X1F?pQmptsxiuggZdulS#fn)L-i*m;ZKIy>FICec z?Aq$)!#br==8w92>SL9DO=6rtmuiPbv*tN|^$&+Dp`f{@5ht!$bNoIycFhrs4W#pB z+h)*c)yxB`JCLW_=2tY~_v>UDCyE1I9k#+%$gezXlC4)+8%Aw~j*EridDqcXL)fG| zS}a7O0u*ANQ2%%qCGqn6~PdHmMYq*0IFL2G+3(r1q%TgJ#_1NFEEf2jfiQf&i6x!nHdyd@Z`5~pBP#;|q zgSKBhTvZ?_Qn_xJvKo)I+1|v2^5V=Mxr*%5pd_n$=#2b+$PBZ|IN-=B^45# z8mE^qDK*lTH)2Rs7+{=wT^iTxw+IzKyZD2!#>=VndZbFdaHI?3RirgQ7QsJ4zaXNN zeMnfg8-dNe`&J^HBhAtb$JzOS;QYv5-5p77L6zCbQD*Zs8o&d#4@|=ZPH)W zGaA1EGF`Eu1YGOVm<%Aap*|w+f$<4QG767xKC<0n>Fq#lyMs>GwOhDd>Z{>#rvBUI%U=^V(W7aQYaAtkYoS)Jq;j7 zhVZFOTnza>k+?x>!||JPmmx=j0`Ggwz@Juq5@rPUB2i!Z>YKg1;HG`{$!(p1rOWL1e$l#bJd1}X7u1L zGf%1Bx`=goYp#Retasksw}$KZo}(bhFVq@ANfi+8!!Mv|-?P_UL%E-j9s{ZEWlky+ z$*mPYSs;m#D@>Ll)+nMxDDqk78?XaADPC8+CYL=5)#exIB1W)o=2$I3X_!8C7dxn= zXSBH+kAWP%nI)+uunlfR!DSxz7FRkfa06QsP~ltcH)DE)Z2dLPghgI|fPPQDC3~H}6 z>6c33bX63B@=(QTHRQ?dDmvOT9ak8srnPOrNBY*}l)J>mEzrNUb8S=6DT=Y?_xVeA`Q)C=mp?UnOB95x?fnpuc2Hj@`C z&N=8TBwIjGHsW*TyPN!#vrlJK%TB>f(egzln~nja4>RY!s;v%*95L6OXT#r3w|o8Q z%WT0moFEZPY|>Y?PIcp9rl(9-4WJ>VzK~DB(|s^;1t!(VBpQYne*k|Da+J;eNAC zE~bF*X(4n1(Y^}v`lgqm{b?(~lgmt|vo-L|%}mr1swI}T=G!B9iqPbc8QJnxQv$>p zZ+~nnLuLBRuTdnrH|4w0Em*YO$m+1J@n}{1(E)Fx5U7S5cPTjCFZrP{R^9hbl-CIK z%#|xH4Cp_$nce?*^Hd|GKdi)4ecsS9wbo?X*=}@m>(=J})7xR~7NWKY=~a8PZ%wa{ ztz@#x>K-M4O=;+TLr{Ifva30WqCb~ZtP%sLhft0xx3~RP*nF9gmntr#lQN3gTh{z7 zX2-kO*xRxqt!=DFnz?rkZ@o%4w4?(Rke@+cecAGVhrMrH8~phfeE50#znVuU+)iUWNH_FWK!F3RA;zwdMw<^eG`n zmb`hKp~2>)((7ly&(ja=gXVp^mhV`F7Q+`C;f zbf_@*h|E>Oz4}Y{5=|#nh`wM9$hkJJ`mEnFh2?YD@F^d`r}2~F6&&pox4=K!`NU|+ zB`C*L+25N3>*Z>>d#SPtWgw&b$&#o%Q;x(Xr8MG}6PMG_uXOD$X16j(6;mGEBSl)j z&rp5`CTk~^O4zf)kW6|)6GEiFraEn&vkyeS1Q6?^bV}P4Iv2(lii>YGD{!a?+yf}1 z-3|l;=&Tc@MeX2n410S8_BWJ%xqI;5#Nk(%m z^$(X0lONFr*h&0)wAWum0(6jD8(y$Tyf*QJ+cHaRRt{2fB&*+R$;m~3{0Kyl=xLyR zq!l+9I>5;Febrz`xZd+@d#l?r$OaoOXPvL8eD$9k|=D3 zvtxNOfLXAS**q%ceARLV@kuFXdQLIq z;`?x@Ok{yvaiH_Bc&yC1q!I(kc(Z&9te#-nP!o<)WKYc0mx^AAXC z&c=7$=Mp^ePGcrj!)6C&@CvlH$rJlE7M#i|XR}=Q&oEUOOcD>WgoqXyK>wriWKGxs8DR|m zO5{kC^}e~N1A{hIKi?Sz&0WZbn>P@YHHA(z;jDoqt6(jI!9W5L-%-u znTD_EX&L4V_uQ-<6FR=;5+Py$(Jdfx=Uvqg3=tF6Vc@DtYGKQ881&MW34r>(BH zMiNNW7Yr9qVMX1uY>$ndaL!VGVGv&=Y?nd_?esQdM{%H*a_X%BUTu<{&T&A?2Rj9SkiK9Z;eXJdqB~0TBQL;t-K+EWnbE2{kV!rSglUYQhfc&`&ez z%!ajB?EX0AsMU#%H5}2NK6g7U&zLY!RFe@@s? zjK$wR`o-G^MrOlw2G+b{WOaOuk%59B^1Tn>y{+M6k9dQ9x*i==Lx>I(j=`7c#ai{i z-X-%SeGLb5R@u$}y^N$Nj6j{OevGXii5?!+B%&&Ml`Gbd(4dxNqXt-|ZeJ`eO^o}t zsZhfe{!vw+(DA}Qk=U>s7iVCo!4-wg(fibGYjugO5T?>GtB8a*OD$}%LkR#SE=Z=N z-I#5%b!*98^+|=W4AP;UBc3BC<^sDHriGq-nH=r#oWm=Q6LA4f`t= zM>ID`?oXPYQsYLJZ-ART&}5$8IJ{iO{8L^*x~nzG#9=e_SguH~6I@8bzsBV^Ng7B( z9Gvzyr0bfhBzGJ5au1$07l!9vdPBLIpPjaLw!XoV0A*(1S0e;OM#n|a4#AYai~708 z#L35&oV$$4E5HO#$fM$GENQK-s6oqofgiuRe^KQ0y)Tl+02^~BddyvD`c{b=G|n}i zBKm=2@)$@~6IX#&<@Rd}vNQD(ca?@Gy@@HBy(Q>pDR5?ip^0=6Zk_(Eiv~|*nIw9v zqth8isr%Psi8_s%Z%+QkIj>%|;hN<1c$`zu7FP;Jx;|bb+m_uLM)GLWv?Kpw8XSzi zj>3CO9u1s_A8bQ1eq*A z&B>@vd#dFtNF^M@)jfz$DS){72Ae{(VH{tGAIl)Y+C<-bi`wtG(!cGnkLXp?N9ZoK zkCWp3DOo~{C=bPVZg(?~AGWVi28Ih)(H`AUkXTL}gwO_T_uX_Wk@MLIzI_X=k(Y%U zOlBqOA{rHNUthlLBFA269Q$RNcFvu0xRIqKcTasDm9 z$Pg9&Y-x*f!KF>!Bs;&W^y&yV8MoK1{muEN(hlF$J6HQHz-K0S289~3?leo#I;lWe zU+$Vk>%RYp2xvZwpFMZ2iWEdisq&->kkOdFM3G7M_b+}s(_Cw?$6psx&xoMvN3P;E zo{slETzfX_o6~y-vP7Mm_@ZGUab04>B{*wPGrSFXoQ3Urw_zBc$p@n6=5g$2sU{XN zkUKFw2`N=!;Jc}AlTF;?jeGxq9y%Cw80|zm?f&1kER6M2M@? z6Atv!Q*`sJMZSI;h(WQRi!-s;S7rI`6L|;9l>WXh#@k9M9Gv8$h_@~nCYSJZgOWmL z3bq8=8TE=o6nVci-B|vN za@uxpl@O;Dv`oHGOL#fAfkI9ii`&eE+Pn?+B@#*cYFClYQkL`6~92>+7vO zzJssS74$KHegs-BX`8r{8Rjn(9ph#RH-b&s7N3iY8rZW8aQn~^DpBMYtgC%y(gFDh zi)?nuEZ}b}F94{7Oi)RJ?ne*D!UJ`;x6X@JzJN zN@Oxr@Dii)98bMR3{*y_D*Yw;3*fz1^Xq;rY<=6EkatlFUAEwFk;1k*<{FkSoiu8| zut~Z8-lOF5!?(#c9wKVepY!urLXv!6{*?R;oSm@V8B~3_Lv`pF^3+1yXldsVPFkl| z4#kXPt!F7kyLoKM*j(K-;fHeYA;5*jK4~=2`Q*ES^ST$yb)yK5?CvG0<$6VwEoQ3YgcV zc6db2EJn>+@>jUcsY5KH1J^nf7c&Ai4fjWvi5$7zBhDJwY7ax+zG`;sQLP=R0d_hH zF@fH!ExLf&9L#-rwas?fx-#XOZ_2iJtg7@!iLg&dduWP&aA0nnjjc8KOCnyjUf?#M zzo?#q7Us4BdNw-NMwYe~)?nNV`27p~jgX%ApBD|UwUL&ogdF%2Q!N7YwO3T6mpaTLKz~n<(W+ng~D+4P7 zGnmoFz(B~zzzq7oAAvb_FXn;?HF`{hAm9sn=YJt>UWjnN5er|^{@)z`Hkk0A9s!eh z__S>FUK0G>Zdp5RTPI6!hwzICyrg&m?J$Ac17`PF2#E+tXju}{i|B%zZ)EHAk1XqC z18)5PaCv0(42-~o;6zBt3myVJFcMM9T3657=mnDaKiR;dtevH$soo1gkq}JOfumP^ zLHVh&{zCv`03+a7>DZW=7?{;xxO!mV9y2pD9Rs*`z;Dmy+>8Gy9=!eX!}g!b8U9zge@g+Y|0(}p_205z$^ZeF>DU0QEG+6TWGyg8>tB<> z^ej~%c%c7P{@wc5lz+82C|Knc&3nKgf$4vrP_rIx0>X9pf-VDgTr*462_Vj@`HMN9% zCaP!J9!d@b?c_R&N?8If>q{2NvMNfv>tdq`Agc|`?Fzs#=>yVIQ6Tw5wV*1_eUdyW zovm32rf7yLa3!Fvun2n0VaFJs;y`rjN+T9_-J@~Estf)nMfA1u4~sJL_oDgQXE977LBF6ApcUYA@_ zJ<)V=hvePW+lWo<`}WFL#tomp#Zz@_qi0^fMYRnexe~a5=?k#(r0mcR9i?u)zr$IEGj|0m?>YJtNKI3|EmnP6W6#~<*N zkh73CHv(q?W4->tbp0EW9If^A;lNWL1P6{||L+AR(*eQ2M18{lXfN2ymlvV=|7ZYk z-1}2wUi+zB<`ji34_+QFn`rAGr^9#oB_c~aBFBrf-G{6h|?hlRm?=b?h z{#_oB?Qi>77+&D3f0W1g0s;L)V|u|({;shCUeLpTXv{Atz&|vm7trGG8aUSft+D=> zwt*P_O2^9Z0!sb8JXXdR2~85zLQ=}(&(nP0%bzqf;t=>>=ThsO9T diff --git a/services/skus/docs/cross-platform.md b/services/skus/docs/cross-platform.md deleted file mode 100644 index a4b334320..000000000 --- a/services/skus/docs/cross-platform.md +++ /dev/null @@ -1,69 +0,0 @@ -# Cross Platform Receipt Validation - -To allow for in-app-purchases the SKUs Service herein allows for submission and -verification of vendor specific proof of purchases. The receipt submission API -consumes a base64 encoded request body, and returns the vendor specific external -identifier if the embedded `raw_receipt` is valid. - -## Receipt Submission API - -The body structure of the receipt submission is a base64 encoded json payload as -shown in the two examples. Within the `skus-sdk` there is a `submit-receipt` method -which will take this base64 encoded json payload and perform a submission to the SKUs -API. The un-encoded json payload for the receipt submission API is shown below. - -```json -{ - "type":[android|ios], - "raw_receipt":"[raw receipt payload from vendor]", - "package":"[android package name of project]", // android only required - "subscription_id":"[android subscription name purchased]" // android only required -} -``` - -### Possible Responses - -- 400 - failed to decode id: id is not a uuid // the order uuid in url is not a valid uuid -- 400 - failed to decode id: id cannot be empty // the order uuid in url is empty -- 400 - failed to decode input base64 // the request data is not base64 encoded -- 400 - failed to decode input json // the b64 decoded payload sent is not json -- 400 - failed to validate structure // the payload json is not well formed -- 400 - failed to validate vendor // vendor in payload json is not ios or android -- 400 - purchase is still pending // vendor says this purchase is still pending, not paid yet -- 400 - purchase is deferred // vendor says this purchase is deferred -- 400 - purchase status unknown // unknown purchase status from vendor -- 400 - failed to verify subscription // vendor error verifying this subscription -- 400 - misconfigured client // issue with configuration on server -- 404 - order not found // the order does not exist -- 500 - failed to store status of order - -### Example Android Submission - -```bash -curl -XPOST https://payment.rewards.brave.software/v1/orders/submit-receipt \ --d'joiYW5kcm9pZCIsInJhd19yZWNla.......' -D - - -HTTP2 200 -Content-Type: application/json -... -{ - "externalId": "[receipt-data]", - "vendor": "android", -} -``` - -## Webhook API - -In order to get updated subscription information for various orders pushed from android we have -created a webhook which takes in the payload structure defined -[here](https://developer.android.com/google/play/billing/rtdn-reference) -and then perform a new validation on the existing receipt as per recommendations from the android -developer documentation. The receipt "externalId" metadata is stored on the order in order -for us to lookup which order the notification is referring to in order to validate the status -of said order and reset the internal status accordingly. - -The webhook routes are: - -``` -/v1/webhooks/android -``` diff --git a/services/skus/handler/handler.go b/services/skus/handler/handler.go deleted file mode 100644 index adccf0ed1..000000000 --- a/services/skus/handler/handler.go +++ /dev/null @@ -1,137 +0,0 @@ -package handler - -import ( - "context" - "encoding/json" - "errors" - "io" - "net/http" - - "github.com/asaskevich/govalidator" - "github.com/go-playground/validator/v10" - - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/requestutils" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -const ( - reqBodyLimit10MB = 10 << 20 - - errSomethingWentWrong model.Error = "something went wrong" -) - -type orderService interface { - CreateOrderFromRequest(ctx context.Context, req model.CreateOrderRequest) (*model.Order, error) - CreateOrder(ctx context.Context, req *model.CreateOrderRequestNew) (*model.Order, error) -} - -type Order struct { - svc orderService - valid *validator.Validate -} - -func NewOrder(svc orderService) *Order { - result := &Order{ - svc: svc, - valid: validator.New(), - } - - return result -} - -func (h *Order) Create(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - - var req model.CreateOrderRequest - if err := requestutils.ReadJSON(ctx, r.Body, &req); err != nil { - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) - } - - if _, err := govalidator.ValidateStruct(req); err != nil { - return handlers.WrapValidationError(err) - } - - if len(req.Items) == 0 { - return handlers.ValidationError( - "Error validating request body", - map[string]interface{}{ - "items": "array must contain at least one item", - }, - ) - } - - lg := logging.Logger(ctx, "payments").With().Str("func", "CreateOrderHandler").Logger() - - // The SKU is validated in CreateOrderItemFromMacaroon. - order, err := h.svc.CreateOrderFromRequest(ctx, req) - if err != nil { - if errors.Is(err, model.ErrInvalidSKU) { - lg.Error().Err(err).Msg("invalid sku") - return handlers.ValidationError(err.Error(), nil) - } - - lg.Error().Err(err).Msg("error creating the order") - return handlers.WrapError(err, "Error creating the order in the database", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, order, w, http.StatusCreated) -} - -func (h *Order) CreateNew(w http.ResponseWriter, r *http.Request) *handlers.AppError { - raw, err := io.ReadAll(io.LimitReader(r.Body, reqBodyLimit10MB)) - if err != nil { - return handlers.WrapError(err, "Failed to read request body", http.StatusBadRequest) - } - - req := &model.CreateOrderRequestNew{} - if err := json.Unmarshal(raw, req); err != nil { - return handlers.WrapError(err, "Failed to deserialize request", http.StatusBadRequest) - } - - ctx := r.Context() - - if err := h.valid.StructCtx(ctx, req); err != nil { - verrs, ok := collectValidationErrors(err) - if !ok { - return handlers.WrapError(err, "Failed to validate request", http.StatusBadRequest) - } - - return &handlers.AppError{ - Message: "Validation failed", - Code: http.StatusBadRequest, - Data: map[string]interface{}{"validationErrors": verrs}, - } - } - - lg := logging.Logger(ctx, "payments").With().Str("func", "CreateOrderNew").Logger() - - result, err := h.svc.CreateOrder(ctx, req) - if err != nil { - lg.Error().Err(err).Msg("failed to create order") - - if errors.Is(err, model.ErrInvalidOrderRequest) { - return handlers.WrapError(err, "Invalid order data supplied", http.StatusUnprocessableEntity) - } - - return handlers.WrapError(errSomethingWentWrong, "Couldn't finish creating order", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, result, w, http.StatusCreated) -} - -func collectValidationErrors(err error) (map[string]string, bool) { - var verr validator.ValidationErrors - if !errors.As(err, &verr) { - return nil, false - } - - result := make(map[string]string, len(verr)) - for i := range verr { - result[verr[i].Field()] = verr[i].Error() - } - - return result, true -} diff --git a/services/skus/handler/handler_pvt_test.go b/services/skus/handler/handler_pvt_test.go deleted file mode 100644 index 814122a5f..000000000 --- a/services/skus/handler/handler_pvt_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package handler - -import ( - "context" - "testing" - - "github.com/go-playground/validator/v10" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -func TestCollectValidationErrors_CreateOrderRequestNew(t *testing.T) { - type tcExpected struct { - result map[string]string - ok bool - noErr bool - } - - type testCase struct { - name string - given any - exp tcExpected - } - - tests := []testCase{ - { - name: "invalid_type", - given: map[string]struct{}{}, - exp: tcExpected{}, - }, - - { - name: "no_errors_01", - given: &model.CreateOrderRequestNew{ - Email: "you@example.com", - Currency: "USD", - StripeMetadata: &model.OrderStripeMetadata{ - SuccessURI: "https://example.com/success", - CancelURI: "https://example.com/cancel", - }, - PaymentMethods: []string{"stripe"}, - Items: []model.OrderItemRequestNew{ - { - Quantity: 1, - SKU: "sku", - Location: "location", - Description: "description", - CredentialType: "credential_type", - CredentialValidDuration: "P1M", - StripeMetadata: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - }, - }, - }, - exp: tcExpected{noErr: true}, - }, - - { - name: "one_field", - given: &model.CreateOrderRequestNew{ - Email: "you_example.com", - Currency: "USD", - StripeMetadata: &model.OrderStripeMetadata{ - SuccessURI: "https://example.com/success", - CancelURI: "https://example.com/cancel", - }, - PaymentMethods: []string{"stripe"}, - Items: []model.OrderItemRequestNew{ - { - Quantity: 1, - SKU: "sku", - Location: "location", - Description: "description", - CredentialType: "credential_type", - CredentialValidDuration: "P1M", - StripeMetadata: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - }, - }, - }, - exp: tcExpected{ - result: map[string]string{ - "Email": "Key: 'CreateOrderRequestNew.Email' Error:Field validation for 'Email' failed on the 'email' tag", - }, - ok: true, - }, - }, - - { - name: "few_fields", - given: &model.CreateOrderRequestNew{ - Email: "you_example.com", - Currency: "USDx", - StripeMetadata: &model.OrderStripeMetadata{ - SuccessURI: "https://example.com/success", - CancelURI: "sdsds", - }, - PaymentMethods: []string{"stripe"}, - Items: []model.OrderItemRequestNew{ - { - Quantity: 1, - SKU: "sku", - Location: "location", - Description: "description", - CredentialType: "credential_type", - CredentialValidDuration: "P1M", - StripeMetadata: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - }, - }, - }, - exp: tcExpected{ - result: map[string]string{ - "Email": "Key: 'CreateOrderRequestNew.Email' Error:Field validation for 'Email' failed on the 'email' tag", - "Currency": "Key: 'CreateOrderRequestNew.Currency' Error:Field validation for 'Currency' failed on the 'iso4217' tag", - "CancelURI": "Key: 'CreateOrderRequestNew.StripeMetadata.CancelURI' Error:Field validation for 'CancelURI' failed on the 'http_url' tag", - }, - ok: true, - }, - }, - } - - valid := validator.New() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - verr := valid.StructCtx(context.TODO(), tc.given) - must.Equal(t, tc.exp.noErr, verr == nil) - - act, ok := collectValidationErrors(verr) - - should.Equal(t, tc.exp.ok, ok) - should.Equal(t, tc.exp.result, act) - }) - } -} diff --git a/services/skus/handler/handler_test.go b/services/skus/handler/handler_test.go deleted file mode 100644 index 5ba2527d9..000000000 --- a/services/skus/handler/handler_test.go +++ /dev/null @@ -1,518 +0,0 @@ -package handler_test - -import ( - "bytes" - "context" - "database/sql" - "encoding/json" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/rs/zerolog" - "github.com/shopspring/decimal" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/handlers" - - "github.com/brave-intl/bat-go/services/skus/handler" - "github.com/brave-intl/bat-go/services/skus/model" -) - -func TestMain(m *testing.M) { - zerolog.SetGlobalLevel(zerolog.Disabled) - os.Exit(m.Run()) -} - -type mockOrderService struct { - fnCreateOrderFromRequest func(ctx context.Context, req model.CreateOrderRequest) (*model.Order, error) - fnCreateOrder func(ctx context.Context, req *model.CreateOrderRequestNew) (*model.Order, error) -} - -func (s *mockOrderService) CreateOrderFromRequest(ctx context.Context, req model.CreateOrderRequest) (*model.Order, error) { - if s.fnCreateOrderFromRequest == nil { - return &model.Order{Items: []model.OrderItem{{}}}, nil - } - - return s.fnCreateOrderFromRequest(ctx, req) -} - -func (s *mockOrderService) CreateOrder(ctx context.Context, req *model.CreateOrderRequestNew) (*model.Order, error) { - if s.fnCreateOrder == nil { - return &model.Order{Items: []model.OrderItem{{}}}, nil - } - - return s.fnCreateOrder(ctx, req) -} - -func TestOrder_Create(t *testing.T) { - type tcGiven struct { - svc *mockOrderService - body string - } - - type tcExpected struct { - err *handlers.AppError - result *model.Order - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "invalid_data", - given: tcGiven{ - svc: &mockOrderService{}, - body: `{ - "email": "you@example.com", - "items": [] - }`, - }, - exp: tcExpected{ - err: handlers.ValidationError( - "Error validating request body", - map[string]interface{}{ - "items": "array must contain at least one item", - }, - ), - }, - }, - - { - name: "invalid_sku", - given: tcGiven{ - svc: &mockOrderService{ - fnCreateOrderFromRequest: func(ctx context.Context, req model.CreateOrderRequest) (*model.Order, error) { - return nil, model.ErrInvalidSKU - }, - }, - body: `{ - "email": "you@example.com", - "items": [ - { - "sku": "invalid_sku", - "quantity": 1 - } - ] - }`, - }, - exp: tcExpected{ - err: handlers.ValidationError(model.ErrInvalidSKU.Error(), nil), - }, - }, - - { - name: "some_error", - given: tcGiven{ - svc: &mockOrderService{ - fnCreateOrderFromRequest: func(ctx context.Context, req model.CreateOrderRequest) (*model.Order, error) { - return nil, model.Error("some_error") - }, - }, - body: `{ - "email": "you@example.com", - "items": [ - { - "sku": "invalid_sku", - "quantity": 1 - } - ] - }`, - }, - exp: tcExpected{ - err: handlers.WrapError( - model.Error("some_error"), - "Error creating the order in the database", - http.StatusInternalServerError, - ), - }, - }, - - { - name: "success", - given: tcGiven{ - svc: &mockOrderService{ - fnCreateOrderFromRequest: func(ctx context.Context, req model.CreateOrderRequest) (*model.Order, error) { - result := &model.Order{ - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "somewhere", - }, - }, - Items: []model.OrderItem{ - { - SKU: "some_sku", - Quantity: 1, - Price: mustDecimalFromString("2"), - Subtotal: mustDecimalFromString("2"), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "somewhere", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "something", - }, - }, - }, - }, - TotalPrice: mustDecimalFromString("2"), - } - - return result, nil - }, - }, - body: `{ - "email": "you@example.com", - "items": [ - { - "sku": "some_sku", - "quantity": 1 - } - ] - }`, - }, - exp: tcExpected{ - result: &model.Order{ - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "somewhere", - }, - }, - Items: []model.OrderItem{ - { - SKU: "some_sku", - Quantity: 1, - Price: mustDecimalFromString("2"), - Subtotal: mustDecimalFromString("2"), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "somewhere", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "something", - }, - }, - }, - }, - TotalPrice: mustDecimalFromString("2"), - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - h := handler.NewOrder(tc.given.svc) - - body := bytes.NewBufferString(tc.given.body) - - req := httptest.NewRequest(http.MethodPost, "http://localhost", body) - - rw := httptest.NewRecorder() - rw.Header().Set("content-type", "application/json") - - act1 := h.Create(rw, req) - must.Equal(t, tc.exp.err, act1) - - if tc.exp.err != nil { - act1.ServeHTTP(rw, req) - resp := rw.Body.Bytes() - - act2 := &handlers.AppError{} - err := json.Unmarshal(resp, act2) - must.Equal(t, nil, err) - - // Cause is excluded from JSON. - tc.exp.err.Cause = nil - - should.Equal(t, tc.exp.err, act2) - - return - } - - resp := rw.Body.Bytes() - act2 := &model.Order{} - - err := json.Unmarshal(resp, act2) - must.Equal(t, nil, err) - - should.Equal(t, tc.exp.result, act2) - }) - } -} - -func TestOrder_CreateNew(t *testing.T) { - type tcGiven struct { - svc *mockOrderService - body string - } - - type tcExpected struct { - err *handlers.AppError - result *model.Order - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "invalid_email", - given: tcGiven{ - svc: &mockOrderService{}, - body: `{ - "email": "you_example.com", - "currency": "USD", - "stripe_metadata": { - "success_uri": "https://example.com/success", - "cancel_uri": "https://example.com/cancel" - }, - "payment_methods": ["stripe"], - "items": [ - { - "quantity": 1, - "sku": "sku", - "location": "location", - "description": "description", - "credential_type": "credential_type", - "credential_valid_duration": "P1M", - "stripe_metadata": { - "product_id": "product_id", - "item_id": "item_id" - } - } - ] - }`, - }, - exp: tcExpected{ - err: &handlers.AppError{ - Message: "Validation failed", - Code: http.StatusBadRequest, - Data: map[string]interface{}{"validationErrors": map[string]string{ - "Email": "Key: 'CreateOrderRequestNew.Email' Error:Field validation for 'Email' failed on the 'email' tag", - }}, - }, - }, - }, - - { - name: "some_error", - given: tcGiven{ - svc: &mockOrderService{ - fnCreateOrder: func(ctx context.Context, req *model.CreateOrderRequestNew) (*model.Order, error) { - return nil, model.Error("some_error") - }, - }, - body: `{ - "email": "you@example.com", - "currency": "USD", - "stripe_metadata": { - "success_uri": "https://example.com/success", - "cancel_uri": "https://example.com/cancel" - }, - "payment_methods": ["stripe"], - "items": [ - { - "quantity": 1, - "sku": "sku", - "location": "location", - "description": "description", - "credential_type": "credential_type", - "credential_valid_duration": "P1M", - "stripe_metadata": { - "product_id": "product_id", - "item_id": "item_id" - } - } - ] - }`, - }, - exp: tcExpected{ - err: handlers.WrapError( - model.Error("something went wrong"), - "Couldn't finish creating order", - http.StatusInternalServerError, - ), - }, - }, - - { - name: "success", - given: tcGiven{ - svc: &mockOrderService{ - fnCreateOrder: func(ctx context.Context, req *model.CreateOrderRequestNew) (*model.Order, error) { - result := &model.Order{ - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Items: []model.OrderItem{ - { - SKU: "sku", - Quantity: 1, - Price: mustDecimalFromString("1"), - Subtotal: mustDecimalFromString("1"), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "description", - }, - }, - CredentialType: "credential_type", - ValidForISO: ptrTo("P1M"), - Metadata: datastore.Metadata{ - "stripe_product_id": "product_id", - "stripe_item_id": "item_id", - }, - }, - }, - TotalPrice: mustDecimalFromString("1"), - } - - return result, nil - }, - }, - body: `{ - "email": "you@example.com", - "currency": "USD", - "stripe_metadata": { - "success_uri": "https://example.com/success", - "cancel_uri": "https://example.com/cancel" - }, - "payment_methods": ["stripe"], - "items": [ - { - "quantity": 1, - "sku": "sku", - "location": "location", - "description": "description", - "credential_type": "credential_type", - "credential_valid_duration": "P1M", - "stripe_metadata": { - "product_id": "product_id", - "item_id": "item_id" - } - } - ] - }`, - }, - exp: tcExpected{ - result: &model.Order{ - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Items: []model.OrderItem{ - { - SKU: "sku", - Quantity: 1, - Price: mustDecimalFromString("1"), - Subtotal: mustDecimalFromString("1"), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "description", - }, - }, - CredentialType: "credential_type", - ValidForISO: ptrTo("P1M"), - Metadata: datastore.Metadata{ - "stripe_product_id": "product_id", - "stripe_item_id": "item_id", - }, - }, - }, - TotalPrice: mustDecimalFromString("1"), - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - h := handler.NewOrder(tc.given.svc) - - body := bytes.NewBufferString(tc.given.body) - - req := httptest.NewRequest(http.MethodPost, "http://localhost", body) - - rw := httptest.NewRecorder() - rw.Header().Set("content-type", "application/json") - - act1 := h.CreateNew(rw, req) - must.Equal(t, tc.exp.err, act1) - - if tc.exp.err != nil { - act1.ServeHTTP(rw, req) - resp := rw.Body.Bytes() - - exp, err := json.Marshal(tc.exp.err) - must.Equal(t, nil, err) - - should.Equal(t, exp, bytes.TrimSpace(resp)) - return - } - - resp := rw.Body.Bytes() - act2 := &model.Order{} - - err := json.Unmarshal(resp, act2) - must.Equal(t, nil, err) - - should.Equal(t, tc.exp.result, act2) - }) - } -} - -func mustDecimalFromString(v string) decimal.Decimal { - result, err := decimal.NewFromString(v) - if err != nil { - panic(err) - } - - return result -} - -func ptrTo[T any](v T) *T { - return &v -} diff --git a/services/skus/helpers.go b/services/skus/helpers.go deleted file mode 100644 index 8854a94cd..000000000 --- a/services/skus/helpers.go +++ /dev/null @@ -1 +0,0 @@ -package skus diff --git a/services/skus/input.go b/services/skus/input.go deleted file mode 100644 index 7ade6c47a..000000000 --- a/services/skus/input.go +++ /dev/null @@ -1,491 +0,0 @@ -package skus - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - - "github.com/asaskevich/govalidator" - "github.com/awa/go-iap/appstore" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/square/go-jose" -) - -// VerifyCredentialRequestV1 includes an opaque subscription credential blob -type VerifyCredentialRequestV1 struct { - Type string `json:"type" valid:"in(single-use|time-limited|time-limited-v2)"` - Version float64 `json:"version" valid:"-"` - SKU string `json:"sku" valid:"-"` - MerchantID string `json:"merchantId" valid:"-"` - Presentation string `json:"presentation" valid:"base64"` -} - -// GetSku - implement credential interface -func (vcr *VerifyCredentialRequestV1) GetSku(ctx context.Context) string { - return vcr.SKU -} - -// GetType - implement credential interface -func (vcr *VerifyCredentialRequestV1) GetType(ctx context.Context) string { - return vcr.Type -} - -// GetMerchantID - implement credential interface -func (vcr *VerifyCredentialRequestV1) GetMerchantID(ctx context.Context) string { - return vcr.MerchantID -} - -// GetPresentation - implement credential interface -func (vcr *VerifyCredentialRequestV1) GetPresentation(ctx context.Context) string { - return vcr.Presentation -} - -// VerifyCredentialRequestV2 includes an opaque subscription credential blob -type VerifyCredentialRequestV2 struct { - SKU string `json:"sku" valid:"-"` - MerchantID string `json:"merchantId" valid:"-"` - Credential string `json:"credential" valid:"base64"` - CredentialOpaque *VerifyCredentialOpaque `json:"-" valid:"-"` -} - -// GetSku - implement credential interface -func (vcr *VerifyCredentialRequestV2) GetSku(ctx context.Context) string { - return vcr.SKU -} - -// GetType - implement credential interface -func (vcr *VerifyCredentialRequestV2) GetType(ctx context.Context) string { - if vcr.CredentialOpaque == nil { - return "" - } - return vcr.CredentialOpaque.Type -} - -// GetMerchantID - implement credential interface -func (vcr *VerifyCredentialRequestV2) GetMerchantID(ctx context.Context) string { - return vcr.MerchantID -} - -// GetPresentation - implement credential interface -func (vcr *VerifyCredentialRequestV2) GetPresentation(ctx context.Context) string { - if vcr.CredentialOpaque == nil { - return "" - } - return vcr.CredentialOpaque.Presentation -} - -// Decode - implement Decodable interface -func (vcr *VerifyCredentialRequestV2) Decode(ctx context.Context, data []byte) error { - logger := logging.Logger(ctx, "VerifyCredentialRequestV2.Decode") - logger.Debug().Msg("starting VerifyCredentialRequestV2.Decode") - var err error - - if err := json.Unmarshal(data, vcr); err != nil { - return fmt.Errorf("failed to json decode credential request payload: %w", err) - } - // decode the opaque credential - if vcr.CredentialOpaque, err = credentialOpaqueFromString(vcr.Credential); err != nil { - return fmt.Errorf("failed to decode opaque credential payload: %w", err) - } - return nil -} - -// Validate - implement Validable interface -func (vcr *VerifyCredentialRequestV2) Validate(ctx context.Context) error { - logger := logging.Logger(ctx, "VerifyCredentialRequestV2.Validate") - var err error - for _, v := range []interface{}{vcr, vcr.CredentialOpaque} { - _, err = govalidator.ValidateStruct(v) - if err != nil { - logger.Error().Err(err).Msg("failed to validate request") - return fmt.Errorf("failed to validate verify credential request: %w", err) - } - } - return nil -} - -// VerifyCredentialOpaque includes an opaque presentation blob -type VerifyCredentialOpaque struct { - Type string `json:"type" valid:"in(single-use|time-limited|time-limited-v2)"` - Version float64 `json:"version" valid:"-"` - Presentation string `json:"presentation" valid:"base64"` -} - -// credentialOpaqueFromString - given a base64 encoded "credential" unmarshal into a VerifyCredentialOpaque -func credentialOpaqueFromString(s string) (*VerifyCredentialOpaque, error) { - d, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return nil, fmt.Errorf("failed to base64 decode credential payload: %w", err) - } - var vcp = new(VerifyCredentialOpaque) - if err = json.Unmarshal(d, vcp); err != nil { - return nil, fmt.Errorf("failed to json decode credential payload: %w", err) - } - return vcp, nil -} - -const ( - appleVendor Vendor = "ios" - googleVendor = "android" -) - -var errInvalidVendor = errors.New("invalid vendor") - -// Vendor vendor url input param -type Vendor string - -// String - stringer implementation -func (v *Vendor) String() string { - return string(*v) -} - -// Validate - take raw []byte input and populate id with the ID -func (v *Vendor) Validate(ctx context.Context) error { - if *v != appleVendor && *v != googleVendor { - return fmt.Errorf("%s is not a valid vendor: %w", v, errInvalidVendor) - } - return nil -} - -// Decode - take raw []byte input and populate id with the ID -func (v *Vendor) Decode(ctx context.Context, input []byte) error { - if len(input) == 0 { - return inputs.ErrIDDecodeEmpty - } - *v = Vendor(string(input)) - return nil -} - -// SubmitReceiptRequestV1 - receipt submission request -type SubmitReceiptRequestV1 struct { - Type Vendor `json:"type" valid:"in(ios|android)"` - Blob string `json:"raw_receipt" valid:"required"` - Package string `json:"package" valid:"-"` - SubscriptionID string `json:"subscription_id" valid:"-"` -} - -// Decode - take raw input and populate the struct -func (srrv1 *SubmitReceiptRequestV1) Decode(ctx context.Context, input []byte) error { - buf := make([]byte, base64.StdEncoding.DecodedLen(len(input))) - - // base64 decode the bytes - n, err := base64.StdEncoding.Decode(buf, input) - if err != nil { - return fmt.Errorf("failed to decode input base64: %w", err) - } - // read the json values - if err := json.Unmarshal(buf[:n], srrv1); err != nil { - return fmt.Errorf("failed to decode input json: %w", err) - } - return nil -} - -// Validate - validate the struct -func (srrv1 *SubmitReceiptRequestV1) Validate(ctx context.Context) error { - // validate struct - if _, err := govalidator.ValidateStruct(srrv1); err != nil { - return fmt.Errorf("failed to validate structure: %w", err) - } - if err := srrv1.Type.Validate(ctx); err != nil { - return fmt.Errorf("failed to validate vendor: %w", err) - } - return nil -} - -const ( - androidSubscriptionUnknown = iota - androidSubscriptionRecovered - androidSubscriptionRenewed - androidSubscriptionCanceled - androidSubscriptionPurchased - androidSubscriptionOnHold - androidSubscriptionInGracePeriod - androidSubscriptionRestarted - androidSubscriptionPriceChangeConfirmed - androidSubscriptionDeferred - androidSubscriptionPaused - androidSubscriptionPausedScheduleChanged - androidSubscriptionRevoked - androidSubscriptionExpired -) - -// SubscriptionNotification - an android subscription notification -type SubscriptionNotification struct { - Version string `json:"version"` - NotificationType int `json:"notificationType"` - PurchaseToken string `json:"purchaseToken"` - SubscriptionID string `json:"subscriptionId"` -} - -// DeveloperNotification - developer notification details from AndroidNotificationMessage.Data -type DeveloperNotification struct { - Version string `json:"version"` - PackageName string `json:"packageName"` - SubscriptionNotification SubscriptionNotification `json:"subscriptionNotification"` -} - -// AndroidNotificationMessageAttrs - attributes of a notification message -type AndroidNotificationMessageAttrs map[string]string - -// AndroidNotificationMessage - wrapping structure of an android notification -type AndroidNotificationMessage struct { - Attributes AndroidNotificationMessageAttrs `json:"attributes" valid:"-"` - Data string `json:"data" valid:"base64"` - MessageID string `json:"messageId" valid:"-"` -} - -// Decode - implement Decodable interface -func (anm *AndroidNotificationMessage) Decode(ctx context.Context, data []byte) error { - logger := logging.Logger(ctx, "AndroidNotificationMessage.Decode") - logger.Debug().Msg("starting AndroidNotificationMessage.Decode") - - if err := json.Unmarshal(data, anm); err != nil { - return fmt.Errorf("failed to json decode android notification message: %w", err) - } - return nil -} - -// Validate - implement Validatable interface -func (anm *AndroidNotificationMessage) Validate(ctx context.Context) error { - logger := logging.Logger(ctx, "AndroidNotificationMessage.Validate") - if _, err := govalidator.ValidateStruct(anm); err != nil { - logger.Error().Err(err).Msg("failed to validate request") - return fmt.Errorf("failed to validate android notification message: %w", err) - } - return nil -} - -// GetDeveloperNotification - Extract the developer notification from the android notification message -func (anm *AndroidNotificationMessage) GetDeveloperNotification() (*DeveloperNotification, error) { - - var devNotification = new(DeveloperNotification) - buf := make([]byte, base64.StdEncoding.DecodedLen(len([]byte(anm.Data)))) - - // base64 decode the bytes - n, err := base64.StdEncoding.Decode(buf, []byte(anm.Data)) - if err != nil { - return nil, fmt.Errorf("failed to decode input base64: %w", err) - } - // read the json values - if err := json.Unmarshal(buf[:n], devNotification); err != nil { - return nil, fmt.Errorf("failed to decode input json: %w", err) - } - return devNotification, nil -} - -// AndroidNotification - wrapping structure of an android notification -type AndroidNotification struct { - Message AndroidNotificationMessage `json:"message" valid:"-"` - Subscription string `json:"subscription" valid:"-"` -} - -// Decode - implement Decodable interface -func (an *AndroidNotification) Decode(ctx context.Context, data []byte) error { - logger := logging.Logger(ctx, "AndroidNotification.Decode") - logger.Debug().Msg("starting AndroidNotification.Decode") - - if err := json.Unmarshal(data, an); err != nil { - return fmt.Errorf("failed to json decode android notification: %w", err) - } - return nil -} - -// Validate - implement Validable interface -func (an *AndroidNotification) Validate(ctx context.Context) error { - logger := logging.Logger(ctx, "AndroidNotification.Validate") - if _, err := govalidator.ValidateStruct(an); err != nil { - logger.Error().Err(err).Msg("failed to validate request") - return fmt.Errorf("failed to validate android notification: %w", err) - } - return nil -} - -// IOSNotification - wrapping structure of an android notification -type IOSNotification struct { - payload []byte `json:"-" valid:"-"` - payloadJWS *jose.JSONWebSignature `json:"-" valid:"-"` - SignedPayload string `json:"signedPayload" valid:"-"` - // signed payload is a JWS the payload of which is a base64 encoded - // responseBodyV2DecodedPayload. The data attribute of this payload is the JWSTransaction -} - -// Decode - implement Decodable interface -func (iosn *IOSNotification) Decode(ctx context.Context, data []byte) error { - logger := logging.Logger(ctx, "IOSNotification.Decode") - logger.Debug().Msg("starting IOSNotification.Decode") - - // json unmarshal the notification - if err := json.Unmarshal(data, iosn); err != nil { - logger.Error().Msg("failed to json unmarshal body") - return errorutils.Wrap(err, "error unmarshalling body") - } - - // parse the jws into payloadJWS from the signed payload - payload, err := jose.ParseSigned(iosn.SignedPayload) - if err != nil { - return fmt.Errorf("failed to parse ios notification: %w", err) - } - - iosn.payloadJWS = payload - - return nil -} - -// Validate - implement Validable interface -func (iosn *IOSNotification) Validate(ctx context.Context) error { - logger := logging.Logger(ctx, "IOSNotification.Validate") - - // extract the public key from the jws - pk, err := extractPublicKey(iosn.SignedPayload) - if err != nil { - return fmt.Errorf("failed to extract public key in request: %w", err) - } - // validate the payloadJWS - payload, err := iosn.payloadJWS.Verify(pk) - if err != nil { - return fmt.Errorf("failed to verify jws payload in request: %w", err) - } - logger.Debug().Msg("validated ios notification") - - iosn.payload = payload - - return nil -} - -// GetRenewalInfo - from request get renewal information -func (iosn *IOSNotification) GetRenewalInfo(ctx context.Context) (*appstore.JWSRenewalInfoDecodedPayload, error) { - var ( - resp = new(appstore.JWSRenewalInfoDecodedPayload) - logger = logging.Logger(ctx, "IOSNotification.GetRenewalInfo") - ) - // get the cert from jws header - rootCertStr, err := extractHeaderByIndex(iosn.SignedPayload, 2) - if err != nil { - return nil, err - } - - intermediaCertStr, err := extractHeaderByIndex(iosn.SignedPayload, 1) - if err != nil { - return nil, err - } - - // verify the cert and intermediates with known root - if err = verifyCert(rootCertStr, intermediaCertStr); err != nil { - return nil, err - } - - // cert is good, extract the public key - pk, err := extractPublicKey(iosn.SignedPayload) - if err != nil { - return nil, err - } - - // extract the payload from - payload, err := iosn.payloadJWS.Verify(pk) - if err != nil { - logger.Warn().Err(err).Msg("failed to verify the notification jws") - return nil, fmt.Errorf("failed to verify the notification JWS: %w", err) - } - logger.Debug().Msgf("raw payload: %s", string(payload)) - - // first get the subscription notification payload decoded - // req.payload is json serialized appstore.SubscriptionNotificationV2DecodedPayload - var snv2dp = new(appstore.SubscriptionNotificationV2DecodedPayload) - if err := json.Unmarshal(payload, snv2dp); err != nil { - logger.Warn().Err(err).Msg("failed to unmarshal notification") - return nil, fmt.Errorf("failed to unmarshal subscription notification v2 decoded: %w", err) - } - - signedRenewalInfo, err := jose.ParseSigned(string(snv2dp.Data.SignedRenewalInfo)) - if err != nil { - logger.Warn().Err(err).Msg("failed to parse jws") - return nil, fmt.Errorf("failed to parse the Signed Renewal Info JWS: %w", err) - } - - // verify - signedRenewalBytes, err := signedRenewalInfo.Verify(pk) - if err != nil { - logger.Warn().Err(err).Msg("failed to verify renewal info") - return nil, fmt.Errorf("failed to verify the Signed Renewal Info JWS: %w", err) - } - - // third json unmarshal the resulting output of the jws into a JWSRenewalInfoDecodedPayload (resp) - if err := json.Unmarshal(signedRenewalBytes, resp); err != nil { - logger.Warn().Err(err).Msg("failed to json parse renewal info") - return nil, fmt.Errorf("failed to json parse the Signed Renewal Info JWS: %w", err) - } - - return resp, nil -} - -// GetTransactionInfo - from request get renewal information -func (iosn *IOSNotification) GetTransactionInfo(ctx context.Context) (*appstore.JWSTransactionDecodedPayload, error) { - var ( - resp = new(appstore.JWSTransactionDecodedPayload) - logger = logging.Logger(ctx, "IOSNotification.GetTransactionInfo") - ) - - // get the cert from jws header - rootCertStr, err := extractHeaderByIndex(iosn.SignedPayload, 2) - if err != nil { - return nil, err - } - - intermediaCertStr, err := extractHeaderByIndex(iosn.SignedPayload, 1) - if err != nil { - return nil, err - } - - // verify the cert and intermediates with known root - if err = verifyCert(rootCertStr, intermediaCertStr); err != nil { - return nil, err - } - - // cert is good, extract the public key - pk, err := extractPublicKey(iosn.SignedPayload) - if err != nil { - return nil, err - } - - // extract the payload from - payload, err := iosn.payloadJWS.Verify(pk) - if err != nil { - logger.Warn().Err(err).Msg("failed to verify the notification jws") - return nil, fmt.Errorf("failed to verify the notification JWS: %w", err) - } - logger.Debug().Msgf("raw payload: %s", string(payload)) - - // first get the subscription notification payload decoded - // req.payload is json serialized appstore.SubscriptionNotificationV2DecodedPayload - var snv2dp = new(appstore.SubscriptionNotificationV2DecodedPayload) - if err := json.Unmarshal(iosn.payload, snv2dp); err != nil { - logger.Warn().Err(err).Msg("failed to unmarshal notification") - return nil, fmt.Errorf("failed to unmarshal subscription notification v2 decoded: %w", err) - } - - // verify the signed transaction jws - signedTransactionInfo, err := jose.ParseSigned(string(snv2dp.Data.SignedTransactionInfo)) - if err != nil { - logger.Warn().Err(err).Msg("failed to parse transaction jws") - return nil, fmt.Errorf("failed to parse the Signed Transaction Info JWS: %w", err) - } - - // verify - signedTransactionBytes, err := signedTransactionInfo.Verify(pk) - if err != nil { - logger.Warn().Err(err).Msg("failed to verify transaction jws") - return nil, fmt.Errorf("failed to verify the Signed Transaction Info JWS: %w", err) - } - - // third json unmarshal the resulting output of the jws into a JWSTransactionDecodedPayload (resp) - if err := json.Unmarshal(signedTransactionBytes, resp); err != nil { - logger.Warn().Err(err).Msg("failed to json parse the transaction") - return nil, fmt.Errorf("failed to json parse the Signed Transaction Info JWS: %w", err) - } - - return resp, nil -} diff --git a/services/skus/instrumented_datastore.go b/services/skus/instrumented_datastore.go deleted file mode 100644 index f79130141..000000000 --- a/services/skus/instrumented_datastore.go +++ /dev/null @@ -1,794 +0,0 @@ -package skus - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/skus -i Datastore -t ../../.prom-gowrap.tmpl -o instrumented_datastore.go -l "" - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/services/skus/model" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// DatastoreWithPrometheus implements Datastore interface with all methods wrapped -// with Prometheus metrics -type DatastoreWithPrometheus struct { - base Datastore - instanceName string -} - -var datastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "skus_datastore_duration_seconds", - Help: "datastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewDatastoreWithPrometheus returns an instance of the Datastore decorated with prometheus summary metric -func NewDatastoreWithPrometheus(base Datastore, instanceName string) DatastoreWithPrometheus { - return DatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// AppendOrderMetadata implements Datastore -func (_d DatastoreWithPrometheus) AppendOrderMetadata(ctx context.Context, up1 *uuid.UUID, s1 string, s2 string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "AppendOrderMetadata", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.AppendOrderMetadata(ctx, up1, s1, s2) -} - -// AppendOrderMetadataInt implements Datastore -func (_d DatastoreWithPrometheus) AppendOrderMetadataInt(ctx context.Context, up1 *uuid.UUID, s1 string, i1 int) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "AppendOrderMetadataInt", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.AppendOrderMetadataInt(ctx, up1, s1, i1) -} - -// AppendOrderMetadataInt64 implements Datastore -func (_d DatastoreWithPrometheus) AppendOrderMetadataInt64(ctx context.Context, up1 *uuid.UUID, s1 string, i1 int64) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "AppendOrderMetadataInt64", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.AppendOrderMetadataInt64(ctx, up1, s1, i1) -} - -// AreTimeLimitedV2CredsSubmitted implements Datastore -func (_d DatastoreWithPrometheus) AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "AreTimeLimitedV2CredsSubmitted", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.AreTimeLimitedV2CredsSubmitted(ctx, blindedCreds...) -} - -// BeginTx implements Datastore -func (_d DatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// CheckExpiredCheckoutSession implements Datastore -func (_d DatastoreWithPrometheus) CheckExpiredCheckoutSession(u1 uuid.UUID) (b1 bool, s1 string, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CheckExpiredCheckoutSession", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CheckExpiredCheckoutSession(u1) -} - -// CommitVote implements Datastore -func (_d DatastoreWithPrometheus) CommitVote(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CommitVote", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CommitVote(ctx, vr, tx) -} - -// CreateKey implements Datastore -func (_d DatastoreWithPrometheus) CreateKey(merchant string, name string, encryptedSecretKey string, nonce string) (kp1 *Key, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateKey(merchant, name, encryptedSecretKey, nonce) -} - -// CreateOrder implements Datastore -func (_d DatastoreWithPrometheus) CreateOrder(ctx context.Context, dbi sqlx.ExtContext, oreq *model.OrderNew, items []model.OrderItem) (op1 *model.Order, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateOrder(ctx, dbi, oreq, items) -} - -// CreateTransaction implements Datastore -func (_d DatastoreWithPrometheus) CreateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (tp1 *Transaction, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "CreateTransaction", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.CreateTransaction(orderID, externalTransactionID, status, currency, kind, amount) -} - -// DeleteKey implements Datastore -func (_d DatastoreWithPrometheus) DeleteKey(id uuid.UUID, delaySeconds int) (kp1 *Key, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "DeleteKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.DeleteKey(id, delaySeconds) -} - -// DeleteSigningOrderRequestOutboxByOrderTx implements Datastore -func (_d DatastoreWithPrometheus) DeleteSigningOrderRequestOutboxByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "DeleteSigningOrderRequestOutboxByOrderTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.DeleteSigningOrderRequestOutboxByOrderTx(ctx, tx, orderID) -} - -// DeleteSingleUseOrderCredsByOrderTx implements Datastore -func (_d DatastoreWithPrometheus) DeleteSingleUseOrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID, isSigned bool) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "DeleteSingleUseOrderCredsByOrderTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.DeleteSingleUseOrderCredsByOrderTx(ctx, tx, orderID, isSigned) -} - -// DeleteTimeLimitedV2OrderCredsByOrderTx implements Datastore -func (_d DatastoreWithPrometheus) DeleteTimeLimitedV2OrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "DeleteTimeLimitedV2OrderCredsByOrderTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.DeleteTimeLimitedV2OrderCredsByOrderTx(ctx, tx, orderID) -} - -// ExternalIDExists implements Datastore -func (_d DatastoreWithPrometheus) ExternalIDExists(ctx context.Context, s1 string) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "ExternalIDExists", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.ExternalIDExists(ctx, s1) -} - -// GetIssuerByPublicKey implements Datastore -func (_d DatastoreWithPrometheus) GetIssuerByPublicKey(publicKey string) (ip1 *Issuer, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetIssuerByPublicKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetIssuerByPublicKey(publicKey) -} - -// GetKey implements Datastore -func (_d DatastoreWithPrometheus) GetKey(id uuid.UUID, showExpired bool) (kp1 *Key, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetKey(id, showExpired) -} - -// GetKeysByMerchant implements Datastore -func (_d DatastoreWithPrometheus) GetKeysByMerchant(merchant string, showExpired bool) (kap1 *[]Key, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetKeysByMerchant", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetKeysByMerchant(merchant, showExpired) -} - -// GetOrder implements Datastore -func (_d DatastoreWithPrometheus) GetOrder(orderID uuid.UUID) (op1 *Order, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOrder(orderID) -} - -// GetOrderByExternalID implements Datastore -func (_d DatastoreWithPrometheus) GetOrderByExternalID(externalID string) (op1 *Order, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOrderByExternalID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOrderByExternalID(externalID) -} - -// GetOrderCreds implements Datastore -func (_d DatastoreWithPrometheus) GetOrderCreds(orderID uuid.UUID, isSigned bool) (oa1 []OrderCreds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOrderCreds", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOrderCreds(orderID, isSigned) -} - -// GetOrderCredsByItemID implements Datastore -func (_d DatastoreWithPrometheus) GetOrderCredsByItemID(orderID uuid.UUID, itemID uuid.UUID, isSigned bool) (op1 *OrderCreds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOrderCredsByItemID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOrderCredsByItemID(orderID, itemID, isSigned) -} - -// GetOrderItem implements Datastore -func (_d DatastoreWithPrometheus) GetOrderItem(ctx context.Context, itemID uuid.UUID) (op1 *OrderItem, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOrderItem", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOrderItem(ctx, itemID) -} - -// GetOutboxMovAvgDurationSeconds implements Datastore -func (_d DatastoreWithPrometheus) GetOutboxMovAvgDurationSeconds() (i1 int64, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetOutboxMovAvgDurationSeconds", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetOutboxMovAvgDurationSeconds() -} - -// GetPagedMerchantTransactions implements Datastore -func (_d DatastoreWithPrometheus) GetPagedMerchantTransactions(ctx context.Context, merchantID uuid.UUID, pagination *inputs.Pagination) (tap1 *[]Transaction, i1 int, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetPagedMerchantTransactions", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetPagedMerchantTransactions(ctx, merchantID, pagination) -} - -// GetSigningOrderRequestOutboxByOrder implements Datastore -func (_d DatastoreWithPrometheus) GetSigningOrderRequestOutboxByOrder(ctx context.Context, orderID uuid.UUID) (sa1 []SigningOrderRequestOutbox, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetSigningOrderRequestOutboxByOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetSigningOrderRequestOutboxByOrder(ctx, orderID) -} - -// GetSigningOrderRequestOutboxByOrderItem implements Datastore -func (_d DatastoreWithPrometheus) GetSigningOrderRequestOutboxByOrderItem(ctx context.Context, itemID uuid.UUID) (sa1 []SigningOrderRequestOutbox, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetSigningOrderRequestOutboxByOrderItem", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetSigningOrderRequestOutboxByOrderItem(ctx, itemID) -} - -// GetSigningOrderRequestOutboxByRequestID implements Datastore -func (_d DatastoreWithPrometheus) GetSigningOrderRequestOutboxByRequestID(ctx context.Context, dbi sqlx.QueryerContext, reqID uuid.UUID) (sp1 *SigningOrderRequestOutbox, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetSigningOrderRequestOutboxByRequestID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetSigningOrderRequestOutboxByRequestID(ctx, dbi, reqID) -} - -// GetSumForTransactions implements Datastore -func (_d DatastoreWithPrometheus) GetSumForTransactions(orderID uuid.UUID) (d1 decimal.Decimal, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetSumForTransactions", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetSumForTransactions(orderID) -} - -// GetTimeLimitedV2OrderCredsByOrder implements Datastore -func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrder(orderID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTimeLimitedV2OrderCredsByOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetTimeLimitedV2OrderCredsByOrder(orderID) -} - -// GetTimeLimitedV2OrderCredsByOrderItem implements Datastore -func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrderItem(itemID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTimeLimitedV2OrderCredsByOrderItem", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetTimeLimitedV2OrderCredsByOrderItem(itemID) -} - -// GetTLV2Creds implements Datastore -func (_d DatastoreWithPrometheus) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTLV2Creds", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetTLV2Creds(ctx, dbi, ordID, itemID, reqID) -} - -// GetTransaction implements Datastore -func (_d DatastoreWithPrometheus) GetTransaction(externalTransactionID string) (tp1 *Transaction, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTransaction", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetTransaction(externalTransactionID) -} - -// GetTransactions implements Datastore -func (_d DatastoreWithPrometheus) GetTransactions(orderID uuid.UUID) (tap1 *[]Transaction, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTransactions", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetTransactions(orderID) -} - -// GetUncommittedVotesForUpdate implements Datastore -func (_d DatastoreWithPrometheus) GetUncommittedVotesForUpdate(ctx context.Context) (tp1 *sqlx.Tx, vpa1 []*VoteRecord, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetUncommittedVotesForUpdate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetUncommittedVotesForUpdate(ctx) -} - -// InsertOrderCredsTx implements Datastore -func (_d DatastoreWithPrometheus) InsertOrderCredsTx(ctx context.Context, tx *sqlx.Tx, creds *OrderCreds) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertOrderCredsTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertOrderCredsTx(ctx, tx, creds) -} - -// InsertSignedOrderCredentialsTx implements Datastore -func (_d DatastoreWithPrometheus) InsertSignedOrderCredentialsTx(ctx context.Context, tx *sqlx.Tx, signedOrderResult *SigningOrderResult) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertSignedOrderCredentialsTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertSignedOrderCredentialsTx(ctx, tx, signedOrderResult) -} - -// InsertSigningOrderRequestOutbox implements Datastore -func (_d DatastoreWithPrometheus) InsertSigningOrderRequestOutbox(ctx context.Context, requestID uuid.UUID, orderID uuid.UUID, itemID uuid.UUID, signingOrderRequest SigningOrderRequest) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertSigningOrderRequestOutbox", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertSigningOrderRequestOutbox(ctx, requestID, orderID, itemID, signingOrderRequest) -} - -// InsertTimeLimitedV2OrderCredsTx implements Datastore -func (_d DatastoreWithPrometheus) InsertTimeLimitedV2OrderCredsTx(ctx context.Context, tx *sqlx.Tx, tlv2 TimeAwareSubIssuedCreds) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertTimeLimitedV2OrderCredsTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertTimeLimitedV2OrderCredsTx(ctx, tx, tlv2) -} - -// InsertVote implements Datastore -func (_d DatastoreWithPrometheus) InsertVote(ctx context.Context, vr VoteRecord) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertVote", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertVote(ctx, vr) -} - -// IsStripeSub implements Datastore -func (_d DatastoreWithPrometheus) IsStripeSub(u1 uuid.UUID) (b1 bool, s1 string, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "IsStripeSub", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.IsStripeSub(u1) -} - -// MarkVoteErrored implements Datastore -func (_d DatastoreWithPrometheus) MarkVoteErrored(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "MarkVoteErrored", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.MarkVoteErrored(ctx, vr, tx) -} - -// Migrate implements Datastore -func (_d DatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements Datastore -func (_d DatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements Datastore -func (_d DatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements Datastore -func (_d DatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements Datastore -func (_d DatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} - -// SendSigningRequest implements Datastore -func (_d DatastoreWithPrometheus) SendSigningRequest(ctx context.Context, signingRequestWriter SigningRequestWriter) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "SendSigningRequest", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.SendSigningRequest(ctx, signingRequestWriter) -} - -// SetOrderPaid implements Datastore -func (_d DatastoreWithPrometheus) SetOrderPaid(ctx context.Context, up1 *uuid.UUID) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "SetOrderPaid", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.SetOrderPaid(ctx, up1) -} - -// SetOrderTrialDays implements Datastore -func (_d DatastoreWithPrometheus) SetOrderTrialDays(ctx context.Context, orderID *uuid.UUID, days int64) (op1 *Order, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "SetOrderTrialDays", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.SetOrderTrialDays(ctx, orderID, days) -} - -// UpdateOrder implements Datastore -func (_d DatastoreWithPrometheus) UpdateOrder(orderID uuid.UUID, status string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "UpdateOrder", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpdateOrder(orderID, status) -} - -// UpdateOrderMetadata implements Datastore -func (_d DatastoreWithPrometheus) UpdateOrderMetadata(orderID uuid.UUID, key string, value string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "UpdateOrderMetadata", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpdateOrderMetadata(orderID, key, value) -} - -// UpdateSigningOrderRequestOutboxTx implements Datastore -func (_d DatastoreWithPrometheus) UpdateSigningOrderRequestOutboxTx(ctx context.Context, tx *sqlx.Tx, requestID uuid.UUID, completedAt time.Time) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "UpdateSigningOrderRequestOutboxTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpdateSigningOrderRequestOutboxTx(ctx, tx, requestID, completedAt) -} - -// UpdateTransaction implements Datastore -func (_d DatastoreWithPrometheus) UpdateTransaction(orderID uuid.UUID, externalTransactionID string, status string, currency string, kind string, amount decimal.Decimal) (tp1 *Transaction, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "UpdateTransaction", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpdateTransaction(orderID, externalTransactionID, status, currency, kind, amount) -} diff --git a/services/skus/key.go b/services/skus/key.go deleted file mode 100644 index b16d4c0e0..000000000 --- a/services/skus/key.go +++ /dev/null @@ -1,232 +0,0 @@ -package skus - -import ( - "context" - "crypto" - "crypto/rand" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "net/http" - "os" - "time" - - uuid "github.com/satori/go.uuid" - - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/middleware" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -const ( - // What the merchant key length should be. - keyLength = 24 - - errInvalidMerchant model.Error = "merchant was missing from context" - errMerchantMismatch model.Error = "Order merchant does not match authentication" - errLocationMismatch model.Error = "Order location does not match authentication" - errUnexpectedSKUCvt model.Error = "SKU caveat is not supported on order endpoints" -) - -var ( - // EncryptionKey for encrypting secrets. - EncryptionKey = os.Getenv("ENCRYPTION_KEY") - - byteEncryptionKey [32]byte -) - -type caveatsCtxKey struct{} -type merchantCtxKey struct{} - -// Key represents a merchant's keys to validate skus -type Key struct { - ID string `json:"id" db:"id"` - Name string `json:"name" db:"name"` - Merchant string `json:"merchant" db:"merchant_id"` - EncryptedSecretKey string `json:"-" db:"encrypted_secret_key"` - Nonce string `json:"-" db:"nonce"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - Expiry *time.Time `json:"expiry" db:"expiry"` -} - -// InitEncryptionKeys copies the specified encryption key into memory once -func InitEncryptionKeys() { - copy(byteEncryptionKey[:], []byte(EncryptionKey)) -} - -// GetSecretKey decrypts the secret key from the database -func (key *Key) GetSecretKey() (*string, error) { - encrypted, err := hex.DecodeString(key.EncryptedSecretKey) - if err != nil { - return nil, err - } - - nonce, err := hex.DecodeString(key.Nonce) - if err != nil { - return nil, err - } - - secretKey, err := cryptography.DecryptMessage(byteEncryptionKey, encrypted, nonce) - if err != nil { - return nil, err - } - - return &secretKey, nil -} - -func randomString(n int) (string, error) { - b := make([]byte, n) - - // Note that err == nil only if we read len(b) bytes. - if _, err := rand.Read(b); err != nil { - return "", err - } - - return base64.RawURLEncoding.EncodeToString(b), nil -} - -// GenerateSecret creates a random key for merchants -func GenerateSecret() (secret string, nonce string, err error) { - unencryptedSecret, err := randomString(keyLength) - if err != nil { - return "", "", err - } - unencryptedSecret = cryptography.SecretTokenPrefix + unencryptedSecret - - encryptedBytes, nonceBytes, err := cryptography.EncryptMessage(byteEncryptionKey, []byte(unencryptedSecret)) - - return fmt.Sprintf("%x", encryptedBytes), fmt.Sprintf("%x", nonceBytes), err -} - -// LookupVerifier returns the merchant key corresponding to the keyID used for verifying requests -func (s *Service) LookupVerifier(ctx context.Context, keyID string) (context.Context, *httpsignature.Verifier, error) { - rootKeyIDStr, caveats, err := cryptography.DecodeKeyID(keyID) - if err != nil { - return nil, nil, err - } - - rootKeyID, err := uuid.FromString(rootKeyIDStr) - if err != nil { - return nil, nil, fmt.Errorf("root key id must be a uuid: %v", err) - } - - key, err := s.Datastore.GetKey(rootKeyID, false) - if err != nil { - return nil, nil, err - } - - secretKey, err := key.GetSecretKey() - if err != nil { - return nil, nil, err - } - if secretKey == nil { - return nil, nil, errors.New("missing secret key") - } - - secretKeyStr := *secretKey - - if caveats != nil { - _, secretKeyStr, err = cryptography.Attenuate(rootKeyID.String(), secretKeyStr, caveats) - if err != nil { - return nil, nil, err - } - - ctx = context.WithValue(ctx, caveatsCtxKey{}, caveats) - } - - ctx = context.WithValue(ctx, merchantCtxKey{}, key.Merchant) - - verifier := httpsignature.Verifier(httpsignature.HMACKey(secretKeyStr)) - return ctx, &verifier, nil -} - -// caveatsFromCtx returns authorized caveats from ctx. -func caveatsFromCtx(ctx context.Context) map[string]string { - caveats, ok := ctx.Value(caveatsCtxKey{}).(map[string]string) - if !ok { - return nil - } - - return caveats -} - -// merchantFromCtx returns an authorized merchant from ctx. -func merchantFromCtx(ctx context.Context) (string, error) { - merchant, ok := ctx.Value(merchantCtxKey{}).(string) - if !ok { - return "", errInvalidMerchant - } - - return merchant, nil -} - -// validateOrderMerchantAndCaveats checks that the current authentication of the request has -// permissions to this order by cross-checking the merchant and caveats in context. -func (s *Service) validateOrderMerchantAndCaveats(ctx context.Context, oid uuid.UUID) error { - merchant, err := merchantFromCtx(ctx) - if err != nil { - return err - } - - order, err := s.orderRepo.Get(ctx, s.Datastore.RawDB(), oid) - if err != nil { - return err - } - - if order.MerchantID != merchant { - return errMerchantMismatch - } - - return validateOrderCvt(order, caveatsFromCtx(ctx)) -} - -// NewAuthMwr returns a handler that authorises requests via http signature or simple tokens. -func NewAuthMwr(ks httpsignature.Keystore) func(http.Handler) http.Handler { - merchantVerifier := httpsignature.ParameterizedKeystoreVerifier{ - SignatureParams: httpsignature.SignatureParams{ - Algorithm: httpsignature.HS2019, - Headers: []string{ - "(request-target)", - "host", - "date", - "digest", - "content-length", - "content-type", - }, - }, - Keystore: ks, - Opts: crypto.Hash(0), - } - - // TODO: Keep only VerifyHTTPSignedOnly after migrating Subscriptions to this method. - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Signature") == "" { - // Assume legacy simple token auth. - ctx := context.WithValue(r.Context(), merchantCtxKey{}, "brave.com") - middleware.SimpleTokenAuthorizedOnly(next).ServeHTTP(w, r.WithContext(ctx)) - return - } - - next = middleware.VerifyDateIsRecent(10*time.Minute, 10*time.Minute)(next) - middleware.VerifyHTTPSignedOnly(merchantVerifier)(next).ServeHTTP(w, r) - }) - } -} - -func validateOrderCvt(ord *model.Order, cvt map[string]string) error { - if loc, ok := cvt["location"]; ok && ord.Location.Valid { - if ord.Location.String != loc { - return errLocationMismatch - } - } - - if _, ok := cvt["sku"]; ok { - return errUnexpectedSKUCvt - } - - return nil -} diff --git a/services/skus/key_test.go b/services/skus/key_test.go deleted file mode 100644 index 19a30623b..000000000 --- a/services/skus/key_test.go +++ /dev/null @@ -1,480 +0,0 @@ -package skus - -import ( - "context" - "crypto" - "database/sql" - "encoding/base64" - "encoding/hex" - "fmt" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - sqlmock "github.com/DATA-DOG/go-sqlmock" - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/middleware" - - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -func TestGenerateSecret(t *testing.T) { - // set up the aes key, typically done with env variable atm - oldEncryptionKey := EncryptionKey - defer func() { - EncryptionKey = oldEncryptionKey - }() - - EncryptionKey = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0" - InitEncryptionKeys() - - var byteEncryptionKey [32]byte - copy(byteEncryptionKey[:], EncryptionKey) - - s, n, err := GenerateSecret() - if err != nil { - t.Error("error in generate secret: ", err) - } - - encrypted, err := hex.DecodeString(s) - if err != nil { - t.Error("error while decoding the encrypted string", err) - } - nonce, err := hex.DecodeString(n) - if err != nil { - t.Error("error while decoding the nonce", err) - } - - if len(nonce) != 24 { - t.Error("Nonce does not have correct length", err) - } - - secretKey, err := cryptography.DecryptMessage(byteEncryptionKey, encrypted, nonce) - if err != nil { - t.Error("error in decrypt secret: ", err) - } - - if !strings.Contains(secretKey, cryptography.SecretTokenPrefix) { - t.Error("secret key is missing prefix") - } - - bareSecretKey := strings.TrimPrefix(secretKey, cryptography.SecretTokenPrefix) - // secretKey is random, so i guess just make sure it is base64? - k, err := base64.RawURLEncoding.DecodeString(bareSecretKey) - if err != nil { - t.Error("error decoding generated secret: ", err) - } - if len(bareSecretKey) != 32 { - t.Error("Secret key does not have correct length", err) - } - if len(k) <= 0 { - t.Error("the key should be bigger than nothing") - } -} - -func TestSecretKey(t *testing.T) { - // set up the aes key, typically done with env variable atm - oldEncryptionKey := EncryptionKey - defer func() { - EncryptionKey = oldEncryptionKey - InitEncryptionKeys() - }() - EncryptionKey = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0" - InitEncryptionKeys() - - var ( - sk, err = randomString(20) - expiry = time.Now().Add(1 * time.Minute) - k = &Key{ - ID: "test-id", - Name: "test-name", - Merchant: "test-merchant", - CreatedAt: time.Now(), - Expiry: &expiry, - } - ) - - if err != nil { - t.Error("failed to generate a secret key: ", err) - } - - encryptedBytes, nonceBytes, err := cryptography.EncryptMessage(byteEncryptionKey, []byte(sk)) - - k.EncryptedSecretKey = fmt.Sprintf("%x", encryptedBytes) - k.Nonce = fmt.Sprintf("%x", nonceBytes) - if err != nil { - t.Error("failed to encrypt secret key: ", err) - } - - skResult, err := k.GetSecretKey() - if err != nil { - t.Error("failed to get secret key: ", err) - } - - // the Secret key should now be plaintext in key, check it out - if skResult == nil || sk != *skResult { - t.Error("expecting initial plaintext secret key to match decrypted secret key") - } - -} - -func TestMerchantSignedMiddleware(t *testing.T) { - db, mock, _ := sqlmock.New() - service := &Service{} - service.Datastore = Datastore( - &Postgres{ - Postgres: datastore.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - orderRepo: repository.NewOrder(), - orderItemRepo: repository.NewOrderItem(), - orderPayHistory: repository.NewOrderPayHistory(), - }, - ) - - // Test that no auth fails - token := "FOO" - middleware.TokenList = []string{token} - - fn1 := func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Should not have gotten here") - } - - authMwr := NewAuthMwr(service) - handler := middleware.BearerToken(authMwr((http.HandlerFunc(fn1)))) - - req, err := http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusForbidden, rr.Code, "request without simple or merchant auth should fail") - - // Test that simple auth works and sets caveats / merchant correctly - - fn2 := func(w http.ResponseWriter, r *http.Request) { - // with simple auth legacy mode there are no caveats - caveats := caveatsFromCtx(r.Context()) - assert.Nil(t, caveats) - - // and the merchant is always brave.com - merchant, err := merchantFromCtx(r.Context()) - assert.NoError(t, err) - assert.Equal(t, merchant, "brave.com") - } - handler = middleware.BearerToken(authMwr(http.HandlerFunc(fn2))) - - req, err = http.NewRequest("GET", "/hello-world", nil) - assert.NoError(t, err) - req.Header.Set("authorization", "Bearer "+token) - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "request with simple auth should succeed") - - expectedMerchant := "brave.software" - expectedCaveats := map[string]string{ - "location": "test.brave.software", - "sku": "test-sku", - } - - // Test that merchant signed works and sets caveats / merchant correctly - fn3 := func(w http.ResponseWriter, r *http.Request) { - caveats := caveatsFromCtx(r.Context()) - assert.Equal(t, caveats, expectedCaveats) - - merchant, err := merchantFromCtx(r.Context()) - assert.NoError(t, err) - assert.Equal(t, merchant, expectedMerchant) - } - handler = middleware.BearerToken(authMwr(http.HandlerFunc(fn3))) - - rootID := "a74b1c17-6e29-4bea-a3d7-fc70aebdfc02" - encSecret, hexNonce, err := GenerateSecret() - assert.NoError(t, err) - - encrypted, err := hex.DecodeString(encSecret) - assert.NoError(t, err) - - nonce, err := hex.DecodeString(hexNonce) - assert.NoError(t, err) - - secretKey, err := cryptography.DecryptMessage(byteEncryptionKey, encrypted, nonce) - assert.NoError(t, err) - - iD, attenuatedSecret, err := cryptography.Attenuate(rootID, secretKey, expectedCaveats) - assert.NoError(t, err) - - var sp httpsignature.SignatureParams - sp.Algorithm = httpsignature.HS2019 - sp.KeyID = iD - sp.Headers = []string{"(request-target)", "host", "date", "digest", "content-length", "content-type"} - - ps := httpsignature.ParameterizedSignator{ - SignatureParams: sp, - Signator: httpsignature.HMACKey(attenuatedSecret), - Opts: crypto.Hash(0), - } - req, err = http.NewRequest("GET", "/hello-world", nil) - req.Header.Set("Date", time.Now().Format(time.RFC1123)) - assert.NoError(t, err) - assert.NoError(t, ps.SignRequest(req)) - - rows := sqlmock.NewRows([]string{ - "id", - "name", - "merchant_id", - "created_at", - "expiry", - "encrypted_secret_key", - "nonce", - }) - rows.AddRow( - rootID, - "test key", - "brave.software", - time.Now(), - nil, - encSecret, - hexNonce, - ) - - mock.ExpectQuery(` -^select (.+) from api_keys* -`). - WithArgs(rootID). - WillReturnRows(rows) - - rr = httptest.NewRecorder() - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "request with merchant auth should succeed") -} - -func TestValidateOrderMerchantAndCaveats(t *testing.T) { - type tcGiven struct { - orderID uuid.UUID - merch string - cvt map[string]string - repo *repository.MockOrder - } - - type testCase struct { - name string - given tcGiven - exp error - } - - tests := []testCase{ - { - name: "invalid_order", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("0fb1d6ba-5d39-4f69-830b-c92c4640c86e")), - merch: "brave.com", - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - return nil, model.ErrOrderNotFound - }, - }, - }, - exp: model.ErrOrderNotFound, - }, - - { - name: "merchant_no_caveats", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - merch: "brave.com", - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - result := &model.Order{ - ID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - Currency: "BAT", - MerchantID: "brave.com", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "test.brave.com", - }, - }, - Status: "paid", - } - - return result, nil - }, - }, - }, - }, - - { - name: "incorrect_merchant", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - merch: "brave.software", - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - result := &model.Order{ - ID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - Currency: "BAT", - MerchantID: "brave.com", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "test.brave.com", - }, - }, - Status: "paid", - } - - return result, nil - }, - }, - }, - exp: errMerchantMismatch, - }, - - { - name: "merchant_location", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - merch: "brave.com", - cvt: map[string]string{"location": "test.brave.com"}, - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - result := &model.Order{ - ID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - Currency: "BAT", - MerchantID: "brave.com", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "test.brave.com", - }, - }, - Status: "paid", - } - - return result, nil - }, - }, - }, - }, - - { - name: "incorrect_location", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - merch: "brave.com", - cvt: map[string]string{"location": "test.brave.software"}, - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - result := &model.Order{ - ID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - Currency: "BAT", - MerchantID: "brave.com", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "test.brave.com", - }, - }, - Status: "paid", - } - - return result, nil - }, - }, - }, - exp: errLocationMismatch, - }, - - { - name: "unexpected_sku", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - merch: "brave.com", - cvt: map[string]string{"location": "test.brave.com", "sku": "some_sku"}, - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - result := &model.Order{ - ID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - Currency: "BAT", - MerchantID: "brave.com", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "test.brave.com", - }, - }, - Status: "paid", - } - - return result, nil - }, - }, - }, - exp: errUnexpectedSKUCvt, - }, - - { - name: "empty_order_location", - given: tcGiven{ - orderID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - merch: "brave.com", - cvt: map[string]string{"location": "test.brave.com"}, - - repo: &repository.MockOrder{ - FnGet: func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - result := &model.Order{ - ID: uuid.Must(uuid.FromString("056bf179-1c07-4787-bd36-db51a83ad139")), - Currency: "BAT", - MerchantID: "brave.com", - Status: "paid", - } - - return result, nil - }, - }, - }, - }, - } - - // Need a database instance in Datastore. - // Not using mocks (as the suppressed return value suggests). - dbi, _, err := sqlmock.New() - must.Equal(t, nil, err) - - ds := &Postgres{ - Postgres: datastore.Postgres{DB: sqlx.NewDb(dbi, "postgres")}, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.WithValue(context.Background(), merchantCtxKey{}, tc.given.merch) - ctx = context.WithValue(ctx, caveatsCtxKey{}, tc.given.cvt) - - svc := &Service{ - Datastore: ds, - orderRepo: tc.given.repo, - } - - err := svc.validateOrderMerchantAndCaveats(ctx, tc.given.orderID) - assert.Equal(t, tc.exp, err) - }) - } -} diff --git a/services/skus/mockcredentials.go b/services/skus/mockcredentials.go deleted file mode 100644 index 228ca2f56..000000000 --- a/services/skus/mockcredentials.go +++ /dev/null @@ -1,141 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./skus/credentials.go - -// Package skus is a generated GoMock package. -package skus - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - go_uuid "github.com/satori/go.uuid" - kafka_go "github.com/segmentio/kafka-go" -) - -// MockOrderWorker is a mock of OrderWorker interface. -type MockOrderWorker struct { - ctrl *gomock.Controller - recorder *MockOrderWorkerMockRecorder -} - -// MockOrderWorkerMockRecorder is the mock recorder for MockOrderWorker. -type MockOrderWorkerMockRecorder struct { - mock *MockOrderWorker -} - -// NewMockOrderWorker creates a new mock instance. -func NewMockOrderWorker(ctrl *gomock.Controller) *MockOrderWorker { - mock := &MockOrderWorker{ctrl: ctrl} - mock.recorder = &MockOrderWorkerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockOrderWorker) EXPECT() *MockOrderWorkerMockRecorder { - return m.recorder -} - -// SignOrderCreds mocks base method. -func (m *MockOrderWorker) SignOrderCreds(ctx context.Context, orderID go_uuid.UUID, issuer Issuer, blindedCreds []string) (*OrderCreds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SignOrderCreds", ctx, orderID, issuer, blindedCreds) - ret0, _ := ret[0].(*OrderCreds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SignOrderCreds indicates an expected call of SignOrderCreds. -func (mr *MockOrderWorkerMockRecorder) SignOrderCreds(ctx, orderID, issuer, blindedCreds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignOrderCreds", reflect.TypeOf((*MockOrderWorker)(nil).SignOrderCreds), ctx, orderID, issuer, blindedCreds) -} - -// MockSigningRequestWriter is a mock of SigningRequestWriter interface. -type MockSigningRequestWriter struct { - ctrl *gomock.Controller - recorder *MockSigningRequestWriterMockRecorder -} - -// MockSigningRequestWriterMockRecorder is the mock recorder for MockSigningRequestWriter. -type MockSigningRequestWriterMockRecorder struct { - mock *MockSigningRequestWriter -} - -// NewMockSigningRequestWriter creates a new mock instance. -func NewMockSigningRequestWriter(ctrl *gomock.Controller) *MockSigningRequestWriter { - mock := &MockSigningRequestWriter{ctrl: ctrl} - mock.recorder = &MockSigningRequestWriterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSigningRequestWriter) EXPECT() *MockSigningRequestWriterMockRecorder { - return m.recorder -} - -// WriteMessage mocks base method. -func (m *MockSigningRequestWriter) WriteMessage(ctx context.Context, message []byte) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WriteMessage", ctx, message) - ret0, _ := ret[0].(error) - return ret0 -} - -// WriteMessage indicates an expected call of WriteMessage. -func (mr *MockSigningRequestWriterMockRecorder) WriteMessage(ctx, message interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteMessage", reflect.TypeOf((*MockSigningRequestWriter)(nil).WriteMessage), ctx, message) -} - -// WriteMessages mocks base method. -func (m *MockSigningRequestWriter) WriteMessages(ctx context.Context, messages []SigningOrderRequestOutbox) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WriteMessages", ctx, messages) - ret0, _ := ret[0].(error) - return ret0 -} - -// WriteMessages indicates an expected call of WriteMessages. -func (mr *MockSigningRequestWriterMockRecorder) WriteMessages(ctx, messages interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteMessages", reflect.TypeOf((*MockSigningRequestWriter)(nil).WriteMessages), ctx, messages) -} - -// MockDecoder is a mock of Decoder interface. -type MockDecoder struct { - ctrl *gomock.Controller - recorder *MockDecoderMockRecorder -} - -// MockDecoderMockRecorder is the mock recorder for MockDecoder. -type MockDecoderMockRecorder struct { - mock *MockDecoder -} - -// NewMockDecoder creates a new mock instance. -func NewMockDecoder(ctrl *gomock.Controller) *MockDecoder { - mock := &MockDecoder{ctrl: ctrl} - mock.recorder = &MockDecoderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDecoder) EXPECT() *MockDecoderMockRecorder { - return m.recorder -} - -// Decode mocks base method. -func (m *MockDecoder) Decode(message kafka_go.Message) (*SigningOrderResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Decode", message) - ret0, _ := ret[0].(*SigningOrderResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Decode indicates an expected call of Decode. -func (mr *MockDecoderMockRecorder) Decode(message interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decode", reflect.TypeOf((*MockDecoder)(nil).Decode), message) -} diff --git a/services/skus/mockdatastore.go b/services/skus/mockdatastore.go deleted file mode 100644 index 68bb8606f..000000000 --- a/services/skus/mockdatastore.go +++ /dev/null @@ -1,1179 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./skus/datastore.go - -// Package skus is a generated GoMock package. -package skus - -import ( - context "context" - reflect "reflect" - time "time" - - datastore "github.com/brave-intl/bat-go/libs/datastore" - inputs "github.com/brave-intl/bat-go/libs/inputs" - model "github.com/brave-intl/bat-go/services/skus/model" - v4 "github.com/golang-migrate/migrate/v4" - gomock "github.com/golang/mock/gomock" - sqlx "github.com/jmoiron/sqlx" - go_uuid "github.com/satori/go.uuid" - decimal "github.com/shopspring/decimal" -) - -// MockDatastore is a mock of Datastore interface. -type MockDatastore struct { - ctrl *gomock.Controller - recorder *MockDatastoreMockRecorder -} - -// MockDatastoreMockRecorder is the mock recorder for MockDatastore. -type MockDatastoreMockRecorder struct { - mock *MockDatastore -} - -// NewMockDatastore creates a new mock instance. -func NewMockDatastore(ctrl *gomock.Controller) *MockDatastore { - mock := &MockDatastore{ctrl: ctrl} - mock.recorder = &MockDatastoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDatastore) EXPECT() *MockDatastoreMockRecorder { - return m.recorder -} - -// AppendOrderMetadata mocks base method. -func (m *MockDatastore) AppendOrderMetadata(arg0 context.Context, arg1 *go_uuid.UUID, arg2, arg3 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendOrderMetadata", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendOrderMetadata indicates an expected call of AppendOrderMetadata. -func (mr *MockDatastoreMockRecorder) AppendOrderMetadata(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendOrderMetadata", reflect.TypeOf((*MockDatastore)(nil).AppendOrderMetadata), arg0, arg1, arg2, arg3) -} - -// AppendOrderMetadataInt mocks base method. -func (m *MockDatastore) AppendOrderMetadataInt(arg0 context.Context, arg1 *go_uuid.UUID, arg2 string, arg3 int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendOrderMetadataInt", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendOrderMetadataInt indicates an expected call of AppendOrderMetadataInt. -func (mr *MockDatastoreMockRecorder) AppendOrderMetadataInt(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendOrderMetadataInt", reflect.TypeOf((*MockDatastore)(nil).AppendOrderMetadataInt), arg0, arg1, arg2, arg3) -} - -// AppendOrderMetadataInt64 mocks base method. -func (m *MockDatastore) AppendOrderMetadataInt64(arg0 context.Context, arg1 *go_uuid.UUID, arg2 string, arg3 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendOrderMetadataInt64", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendOrderMetadataInt64 indicates an expected call of AppendOrderMetadataInt64. -func (mr *MockDatastoreMockRecorder) AppendOrderMetadataInt64(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendOrderMetadataInt64", reflect.TypeOf((*MockDatastore)(nil).AppendOrderMetadataInt64), arg0, arg1, arg2, arg3) -} - -// AreTimeLimitedV2CredsSubmitted mocks base method. -func (m *MockDatastore) AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (bool, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx} - for _, a := range blindedCreds { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "AreTimeLimitedV2CredsSubmitted", varargs...) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AreTimeLimitedV2CredsSubmitted indicates an expected call of AreTimeLimitedV2CredsSubmitted. -func (mr *MockDatastoreMockRecorder) AreTimeLimitedV2CredsSubmitted(ctx interface{}, blindedCreds ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx}, blindedCreds...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AreTimeLimitedV2CredsSubmitted", reflect.TypeOf((*MockDatastore)(nil).AreTimeLimitedV2CredsSubmitted), varargs...) -} - -// BeginTx mocks base method. -func (m *MockDatastore) BeginTx() (*sqlx.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeginTx") - ret0, _ := ret[0].(*sqlx.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BeginTx indicates an expected call of BeginTx. -func (mr *MockDatastoreMockRecorder) BeginTx() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockDatastore)(nil).BeginTx)) -} - -// CheckExpiredCheckoutSession mocks base method. -func (m *MockDatastore) CheckExpiredCheckoutSession(arg0 go_uuid.UUID) (bool, string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckExpiredCheckoutSession", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(string) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CheckExpiredCheckoutSession indicates an expected call of CheckExpiredCheckoutSession. -func (mr *MockDatastoreMockRecorder) CheckExpiredCheckoutSession(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckExpiredCheckoutSession", reflect.TypeOf((*MockDatastore)(nil).CheckExpiredCheckoutSession), arg0) -} - -// CommitVote mocks base method. -func (m *MockDatastore) CommitVote(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CommitVote", ctx, vr, tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// CommitVote indicates an expected call of CommitVote. -func (mr *MockDatastoreMockRecorder) CommitVote(ctx, vr, tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitVote", reflect.TypeOf((*MockDatastore)(nil).CommitVote), ctx, vr, tx) -} - -// CreateKey mocks base method. -func (m *MockDatastore) CreateKey(merchant, name, encryptedSecretKey, nonce string) (*Key, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateKey", merchant, name, encryptedSecretKey, nonce) - ret0, _ := ret[0].(*Key) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateKey indicates an expected call of CreateKey. -func (mr *MockDatastoreMockRecorder) CreateKey(merchant, name, encryptedSecretKey, nonce interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateKey", reflect.TypeOf((*MockDatastore)(nil).CreateKey), merchant, name, encryptedSecretKey, nonce) -} - -// CreateOrder mocks base method. -func (m *MockDatastore) CreateOrder(ctx context.Context, dbi sqlx.ExtContext, req *model.OrderNew, items []model.OrderItem) (*model.Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateOrder", ctx, dbi, req, items) - ret0, _ := ret[0].(*model.Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateOrder indicates an expected call of CreateOrder. -func (mr *MockDatastoreMockRecorder) CreateOrder(ctx, dbi, req, items interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrder", reflect.TypeOf((*MockDatastore)(nil).CreateOrder), ctx, dbi, req, items) -} - -// CreateTransaction mocks base method. -func (m *MockDatastore) CreateTransaction(orderID go_uuid.UUID, externalTransactionID, status, currency, kind string, amount decimal.Decimal) (*Transaction, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateTransaction", orderID, externalTransactionID, status, currency, kind, amount) - ret0, _ := ret[0].(*Transaction) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateTransaction indicates an expected call of CreateTransaction. -func (mr *MockDatastoreMockRecorder) CreateTransaction(orderID, externalTransactionID, status, currency, kind, amount interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTransaction", reflect.TypeOf((*MockDatastore)(nil).CreateTransaction), orderID, externalTransactionID, status, currency, kind, amount) -} - -// DeleteKey mocks base method. -func (m *MockDatastore) DeleteKey(id go_uuid.UUID, delaySeconds int) (*Key, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteKey", id, delaySeconds) - ret0, _ := ret[0].(*Key) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteKey indicates an expected call of DeleteKey. -func (mr *MockDatastoreMockRecorder) DeleteKey(id, delaySeconds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteKey", reflect.TypeOf((*MockDatastore)(nil).DeleteKey), id, delaySeconds) -} - -// DeleteSigningOrderRequestOutboxByOrderTx mocks base method. -func (m *MockDatastore) DeleteSigningOrderRequestOutboxByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID go_uuid.UUID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSigningOrderRequestOutboxByOrderTx", ctx, tx, orderID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteSigningOrderRequestOutboxByOrderTx indicates an expected call of DeleteSigningOrderRequestOutboxByOrderTx. -func (mr *MockDatastoreMockRecorder) DeleteSigningOrderRequestOutboxByOrderTx(ctx, tx, orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSigningOrderRequestOutboxByOrderTx", reflect.TypeOf((*MockDatastore)(nil).DeleteSigningOrderRequestOutboxByOrderTx), ctx, tx, orderID) -} - -// DeleteSingleUseOrderCredsByOrderTx mocks base method. -func (m *MockDatastore) DeleteSingleUseOrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID go_uuid.UUID, isSigned bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSingleUseOrderCredsByOrderTx", ctx, tx, orderID, isSigned) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteSingleUseOrderCredsByOrderTx indicates an expected call of DeleteSingleUseOrderCredsByOrderTx. -func (mr *MockDatastoreMockRecorder) DeleteSingleUseOrderCredsByOrderTx(ctx, tx, orderID, isSigned interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSingleUseOrderCredsByOrderTx", reflect.TypeOf((*MockDatastore)(nil).DeleteSingleUseOrderCredsByOrderTx), ctx, tx, orderID, isSigned) -} - -// DeleteTimeLimitedV2OrderCredsByOrderTx mocks base method. -func (m *MockDatastore) DeleteTimeLimitedV2OrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID go_uuid.UUID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteTimeLimitedV2OrderCredsByOrderTx", ctx, tx, orderID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteTimeLimitedV2OrderCredsByOrderTx indicates an expected call of DeleteTimeLimitedV2OrderCredsByOrderTx. -func (mr *MockDatastoreMockRecorder) DeleteTimeLimitedV2OrderCredsByOrderTx(ctx, tx, orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTimeLimitedV2OrderCredsByOrderTx", reflect.TypeOf((*MockDatastore)(nil).DeleteTimeLimitedV2OrderCredsByOrderTx), ctx, tx, orderID) -} - -// ExternalIDExists mocks base method. -func (m *MockDatastore) ExternalIDExists(arg0 context.Context, arg1 string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExternalIDExists", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExternalIDExists indicates an expected call of ExternalIDExists. -func (mr *MockDatastoreMockRecorder) ExternalIDExists(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExternalIDExists", reflect.TypeOf((*MockDatastore)(nil).ExternalIDExists), arg0, arg1) -} - -// GetIssuerByPublicKey mocks base method. -func (m *MockDatastore) GetIssuerByPublicKey(publicKey string) (*Issuer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetIssuerByPublicKey", publicKey) - ret0, _ := ret[0].(*Issuer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetIssuerByPublicKey indicates an expected call of GetIssuerByPublicKey. -func (mr *MockDatastoreMockRecorder) GetIssuerByPublicKey(publicKey interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuerByPublicKey", reflect.TypeOf((*MockDatastore)(nil).GetIssuerByPublicKey), publicKey) -} - -// GetKey mocks base method. -func (m *MockDatastore) GetKey(id go_uuid.UUID, showExpired bool) (*Key, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetKey", id, showExpired) - ret0, _ := ret[0].(*Key) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetKey indicates an expected call of GetKey. -func (mr *MockDatastoreMockRecorder) GetKey(id, showExpired interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKey", reflect.TypeOf((*MockDatastore)(nil).GetKey), id, showExpired) -} - -// GetKeysByMerchant mocks base method. -func (m *MockDatastore) GetKeysByMerchant(merchant string, showExpired bool) (*[]Key, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetKeysByMerchant", merchant, showExpired) - ret0, _ := ret[0].(*[]Key) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetKeysByMerchant indicates an expected call of GetKeysByMerchant. -func (mr *MockDatastoreMockRecorder) GetKeysByMerchant(merchant, showExpired interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeysByMerchant", reflect.TypeOf((*MockDatastore)(nil).GetKeysByMerchant), merchant, showExpired) -} - -// GetOrder mocks base method. -func (m *MockDatastore) GetOrder(orderID go_uuid.UUID) (*Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrder", orderID) - ret0, _ := ret[0].(*Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOrder indicates an expected call of GetOrder. -func (mr *MockDatastoreMockRecorder) GetOrder(orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrder", reflect.TypeOf((*MockDatastore)(nil).GetOrder), orderID) -} - -// GetOrderByExternalID mocks base method. -func (m *MockDatastore) GetOrderByExternalID(externalID string) (*Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrderByExternalID", externalID) - ret0, _ := ret[0].(*Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOrderByExternalID indicates an expected call of GetOrderByExternalID. -func (mr *MockDatastoreMockRecorder) GetOrderByExternalID(externalID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrderByExternalID", reflect.TypeOf((*MockDatastore)(nil).GetOrderByExternalID), externalID) -} - -// GetOrderCreds mocks base method. -func (m *MockDatastore) GetOrderCreds(orderID go_uuid.UUID, isSigned bool) ([]OrderCreds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrderCreds", orderID, isSigned) - ret0, _ := ret[0].([]OrderCreds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOrderCreds indicates an expected call of GetOrderCreds. -func (mr *MockDatastoreMockRecorder) GetOrderCreds(orderID, isSigned interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrderCreds", reflect.TypeOf((*MockDatastore)(nil).GetOrderCreds), orderID, isSigned) -} - -// GetOrderCredsByItemID mocks base method. -func (m *MockDatastore) GetOrderCredsByItemID(orderID, itemID go_uuid.UUID, isSigned bool) (*OrderCreds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrderCredsByItemID", orderID, itemID, isSigned) - ret0, _ := ret[0].(*OrderCreds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOrderCredsByItemID indicates an expected call of GetOrderCredsByItemID. -func (mr *MockDatastoreMockRecorder) GetOrderCredsByItemID(orderID, itemID, isSigned interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrderCredsByItemID", reflect.TypeOf((*MockDatastore)(nil).GetOrderCredsByItemID), orderID, itemID, isSigned) -} - -// GetOrderItem mocks base method. -func (m *MockDatastore) GetOrderItem(ctx context.Context, itemID go_uuid.UUID) (*OrderItem, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrderItem", ctx, itemID) - ret0, _ := ret[0].(*OrderItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOrderItem indicates an expected call of GetOrderItem. -func (mr *MockDatastoreMockRecorder) GetOrderItem(ctx, itemID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrderItem", reflect.TypeOf((*MockDatastore)(nil).GetOrderItem), ctx, itemID) -} - -// GetOutboxMovAvgDurationSeconds mocks base method. -func (m *MockDatastore) GetOutboxMovAvgDurationSeconds() (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOutboxMovAvgDurationSeconds") - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetOutboxMovAvgDurationSeconds indicates an expected call of GetOutboxMovAvgDurationSeconds. -func (mr *MockDatastoreMockRecorder) GetOutboxMovAvgDurationSeconds() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOutboxMovAvgDurationSeconds", reflect.TypeOf((*MockDatastore)(nil).GetOutboxMovAvgDurationSeconds)) -} - -// GetPagedMerchantTransactions mocks base method. -func (m *MockDatastore) GetPagedMerchantTransactions(ctx context.Context, merchantID go_uuid.UUID, pagination *inputs.Pagination) (*[]Transaction, int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPagedMerchantTransactions", ctx, merchantID, pagination) - ret0, _ := ret[0].(*[]Transaction) - ret1, _ := ret[1].(int) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetPagedMerchantTransactions indicates an expected call of GetPagedMerchantTransactions. -func (mr *MockDatastoreMockRecorder) GetPagedMerchantTransactions(ctx, merchantID, pagination interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPagedMerchantTransactions", reflect.TypeOf((*MockDatastore)(nil).GetPagedMerchantTransactions), ctx, merchantID, pagination) -} - -// GetSigningOrderRequestOutboxByOrder mocks base method. -func (m *MockDatastore) GetSigningOrderRequestOutboxByOrder(ctx context.Context, orderID go_uuid.UUID) ([]SigningOrderRequestOutbox, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSigningOrderRequestOutboxByOrder", ctx, orderID) - ret0, _ := ret[0].([]SigningOrderRequestOutbox) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSigningOrderRequestOutboxByOrder indicates an expected call of GetSigningOrderRequestOutboxByOrder. -func (mr *MockDatastoreMockRecorder) GetSigningOrderRequestOutboxByOrder(ctx, orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigningOrderRequestOutboxByOrder", reflect.TypeOf((*MockDatastore)(nil).GetSigningOrderRequestOutboxByOrder), ctx, orderID) -} - -// GetSigningOrderRequestOutboxByOrderItem mocks base method. -func (m *MockDatastore) GetSigningOrderRequestOutboxByOrderItem(ctx context.Context, itemID go_uuid.UUID) ([]SigningOrderRequestOutbox, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSigningOrderRequestOutboxByOrderItem", ctx, itemID) - ret0, _ := ret[0].([]SigningOrderRequestOutbox) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSigningOrderRequestOutboxByOrderItem indicates an expected call of GetSigningOrderRequestOutboxByOrderItem. -func (mr *MockDatastoreMockRecorder) GetSigningOrderRequestOutboxByOrderItem(ctx, itemID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigningOrderRequestOutboxByOrderItem", reflect.TypeOf((*MockDatastore)(nil).GetSigningOrderRequestOutboxByOrderItem), ctx, itemID) -} - -// GetSigningOrderRequestOutboxByRequestID mocks base method. -func (m *MockDatastore) GetSigningOrderRequestOutboxByRequestID(ctx context.Context, dbi sqlx.QueryerContext, reqID go_uuid.UUID) (*SigningOrderRequestOutbox, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSigningOrderRequestOutboxByRequestID", ctx, dbi, reqID) - ret0, _ := ret[0].(*SigningOrderRequestOutbox) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSigningOrderRequestOutboxByRequestID indicates an expected call of GetSigningOrderRequestOutboxByRequestID. -func (mr *MockDatastoreMockRecorder) GetSigningOrderRequestOutboxByRequestID(ctx, dbi, reqID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigningOrderRequestOutboxByRequestID", reflect.TypeOf((*MockDatastore)(nil).GetSigningOrderRequestOutboxByRequestID), ctx, dbi, reqID) -} - -// GetSumForTransactions mocks base method. -func (m *MockDatastore) GetSumForTransactions(orderID go_uuid.UUID) (decimal.Decimal, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSumForTransactions", orderID) - ret0, _ := ret[0].(decimal.Decimal) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSumForTransactions indicates an expected call of GetSumForTransactions. -func (mr *MockDatastoreMockRecorder) GetSumForTransactions(orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSumForTransactions", reflect.TypeOf((*MockDatastore)(nil).GetSumForTransactions), orderID) -} - -// GetTimeLimitedV2OrderCredsByOrder mocks base method. -func (m *MockDatastore) GetTimeLimitedV2OrderCredsByOrder(orderID go_uuid.UUID) (*TimeLimitedV2Creds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTimeLimitedV2OrderCredsByOrder", orderID) - ret0, _ := ret[0].(*TimeLimitedV2Creds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTimeLimitedV2OrderCredsByOrder indicates an expected call of GetTimeLimitedV2OrderCredsByOrder. -func (mr *MockDatastoreMockRecorder) GetTimeLimitedV2OrderCredsByOrder(orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeLimitedV2OrderCredsByOrder", reflect.TypeOf((*MockDatastore)(nil).GetTimeLimitedV2OrderCredsByOrder), orderID) -} - -// GetTimeLimitedV2OrderCredsByOrderItem mocks base method. -func (m *MockDatastore) GetTimeLimitedV2OrderCredsByOrderItem(itemID go_uuid.UUID) (*TimeLimitedV2Creds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTimeLimitedV2OrderCredsByOrderItem", itemID) - ret0, _ := ret[0].(*TimeLimitedV2Creds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTimeLimitedV2OrderCredsByOrderItem indicates an expected call of GetTimeLimitedV2OrderCredsByOrderItem. -func (mr *MockDatastoreMockRecorder) GetTimeLimitedV2OrderCredsByOrderItem(itemID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeLimitedV2OrderCredsByOrderItem", reflect.TypeOf((*MockDatastore)(nil).GetTimeLimitedV2OrderCredsByOrderItem), itemID) -} - -// GetTLV2Creds mocks base method. -func (m *MockDatastore) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID go_uuid.UUID) (*TimeLimitedV2Creds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTLV2Creds", ctx, dbi, ordID, itemID, reqID) - ret0, _ := ret[0].(*TimeLimitedV2Creds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTLV2Creds indicates an expected call of GetTLV2Creds. -func (mr *MockDatastoreMockRecorder) GetTLV2Creds(ctx, dbi, ordID, itemID, reqID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTLV2Creds", reflect.TypeOf((*MockDatastore)(nil).GetTLV2Creds), ctx, dbi, ordID, itemID, reqID) -} - -// GetTransaction mocks base method. -func (m *MockDatastore) GetTransaction(externalTransactionID string) (*Transaction, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTransaction", externalTransactionID) - ret0, _ := ret[0].(*Transaction) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTransaction indicates an expected call of GetTransaction. -func (mr *MockDatastoreMockRecorder) GetTransaction(externalTransactionID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransaction", reflect.TypeOf((*MockDatastore)(nil).GetTransaction), externalTransactionID) -} - -// GetTransactions mocks base method. -func (m *MockDatastore) GetTransactions(orderID go_uuid.UUID) (*[]Transaction, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTransactions", orderID) - ret0, _ := ret[0].(*[]Transaction) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTransactions indicates an expected call of GetTransactions. -func (mr *MockDatastoreMockRecorder) GetTransactions(orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransactions", reflect.TypeOf((*MockDatastore)(nil).GetTransactions), orderID) -} - -// GetUncommittedVotesForUpdate mocks base method. -func (m *MockDatastore) GetUncommittedVotesForUpdate(ctx context.Context) (*sqlx.Tx, []*VoteRecord, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUncommittedVotesForUpdate", ctx) - ret0, _ := ret[0].(*sqlx.Tx) - ret1, _ := ret[1].([]*VoteRecord) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetUncommittedVotesForUpdate indicates an expected call of GetUncommittedVotesForUpdate. -func (mr *MockDatastoreMockRecorder) GetUncommittedVotesForUpdate(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUncommittedVotesForUpdate", reflect.TypeOf((*MockDatastore)(nil).GetUncommittedVotesForUpdate), ctx) -} - -// InsertOrderCredsTx mocks base method. -func (m *MockDatastore) InsertOrderCredsTx(ctx context.Context, tx *sqlx.Tx, creds *OrderCreds) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertOrderCredsTx", ctx, tx, creds) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertOrderCredsTx indicates an expected call of InsertOrderCredsTx. -func (mr *MockDatastoreMockRecorder) InsertOrderCredsTx(ctx, tx, creds interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOrderCredsTx", reflect.TypeOf((*MockDatastore)(nil).InsertOrderCredsTx), ctx, tx, creds) -} - -// InsertSignedOrderCredentialsTx mocks base method. -func (m *MockDatastore) InsertSignedOrderCredentialsTx(ctx context.Context, tx *sqlx.Tx, signedOrderResult *SigningOrderResult) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertSignedOrderCredentialsTx", ctx, tx, signedOrderResult) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertSignedOrderCredentialsTx indicates an expected call of InsertSignedOrderCredentialsTx. -func (mr *MockDatastoreMockRecorder) InsertSignedOrderCredentialsTx(ctx, tx, signedOrderResult interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertSignedOrderCredentialsTx", reflect.TypeOf((*MockDatastore)(nil).InsertSignedOrderCredentialsTx), ctx, tx, signedOrderResult) -} - -// InsertSigningOrderRequestOutbox mocks base method. -func (m *MockDatastore) InsertSigningOrderRequestOutbox(ctx context.Context, requestID, orderID, itemID go_uuid.UUID, signingOrderRequest SigningOrderRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertSigningOrderRequestOutbox", ctx, requestID, orderID, itemID, signingOrderRequest) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertSigningOrderRequestOutbox indicates an expected call of InsertSigningOrderRequestOutbox. -func (mr *MockDatastoreMockRecorder) InsertSigningOrderRequestOutbox(ctx, requestID, orderID, itemID, signingOrderRequest interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertSigningOrderRequestOutbox", reflect.TypeOf((*MockDatastore)(nil).InsertSigningOrderRequestOutbox), ctx, requestID, orderID, itemID, signingOrderRequest) -} - -// InsertTimeLimitedV2OrderCredsTx mocks base method. -func (m *MockDatastore) InsertTimeLimitedV2OrderCredsTx(ctx context.Context, tx *sqlx.Tx, tlv2 TimeAwareSubIssuedCreds) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertTimeLimitedV2OrderCredsTx", ctx, tx, tlv2) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertTimeLimitedV2OrderCredsTx indicates an expected call of InsertTimeLimitedV2OrderCredsTx. -func (mr *MockDatastoreMockRecorder) InsertTimeLimitedV2OrderCredsTx(ctx, tx, tlv2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTimeLimitedV2OrderCredsTx", reflect.TypeOf((*MockDatastore)(nil).InsertTimeLimitedV2OrderCredsTx), ctx, tx, tlv2) -} - -// InsertVote mocks base method. -func (m *MockDatastore) InsertVote(ctx context.Context, vr VoteRecord) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertVote", ctx, vr) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertVote indicates an expected call of InsertVote. -func (mr *MockDatastoreMockRecorder) InsertVote(ctx, vr interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertVote", reflect.TypeOf((*MockDatastore)(nil).InsertVote), ctx, vr) -} - -// IsStripeSub mocks base method. -func (m *MockDatastore) IsStripeSub(arg0 go_uuid.UUID) (bool, string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsStripeSub", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(string) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// IsStripeSub indicates an expected call of IsStripeSub. -func (mr *MockDatastoreMockRecorder) IsStripeSub(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsStripeSub", reflect.TypeOf((*MockDatastore)(nil).IsStripeSub), arg0) -} - -// MarkVoteErrored mocks base method. -func (m *MockDatastore) MarkVoteErrored(ctx context.Context, vr VoteRecord, tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MarkVoteErrored", ctx, vr, tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// MarkVoteErrored indicates an expected call of MarkVoteErrored. -func (mr *MockDatastoreMockRecorder) MarkVoteErrored(ctx, vr, tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkVoteErrored", reflect.TypeOf((*MockDatastore)(nil).MarkVoteErrored), ctx, vr, tx) -} - -// Migrate mocks base method. -func (m *MockDatastore) Migrate(arg0 ...uint) error { - m.ctrl.T.Helper() - varargs := []interface{}{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Migrate", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// Migrate indicates an expected call of Migrate. -func (mr *MockDatastoreMockRecorder) Migrate(arg0 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Migrate", reflect.TypeOf((*MockDatastore)(nil).Migrate), arg0...) -} - -// NewMigrate mocks base method. -func (m *MockDatastore) NewMigrate() (*v4.Migrate, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewMigrate") - ret0, _ := ret[0].(*v4.Migrate) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NewMigrate indicates an expected call of NewMigrate. -func (mr *MockDatastoreMockRecorder) NewMigrate() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewMigrate", reflect.TypeOf((*MockDatastore)(nil).NewMigrate)) -} - -// RawDB mocks base method. -func (m *MockDatastore) RawDB() *sqlx.DB { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RawDB") - ret0, _ := ret[0].(*sqlx.DB) - return ret0 -} - -// RawDB indicates an expected call of RawDB. -func (mr *MockDatastoreMockRecorder) RawDB() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawDB", reflect.TypeOf((*MockDatastore)(nil).RawDB)) -} - -// RollbackTx mocks base method. -func (m *MockDatastore) RollbackTx(tx *sqlx.Tx) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RollbackTx", tx) -} - -// RollbackTx indicates an expected call of RollbackTx. -func (mr *MockDatastoreMockRecorder) RollbackTx(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTx", reflect.TypeOf((*MockDatastore)(nil).RollbackTx), tx) -} - -// RollbackTxAndHandle mocks base method. -func (m *MockDatastore) RollbackTxAndHandle(tx *sqlx.Tx) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RollbackTxAndHandle", tx) - ret0, _ := ret[0].(error) - return ret0 -} - -// RollbackTxAndHandle indicates an expected call of RollbackTxAndHandle. -func (mr *MockDatastoreMockRecorder) RollbackTxAndHandle(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTxAndHandle", reflect.TypeOf((*MockDatastore)(nil).RollbackTxAndHandle), tx) -} - -// SendSigningRequest mocks base method. -func (m *MockDatastore) SendSigningRequest(ctx context.Context, signingRequestWriter SigningRequestWriter) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendSigningRequest", ctx, signingRequestWriter) - ret0, _ := ret[0].(error) - return ret0 -} - -// SendSigningRequest indicates an expected call of SendSigningRequest. -func (mr *MockDatastoreMockRecorder) SendSigningRequest(ctx, signingRequestWriter interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSigningRequest", reflect.TypeOf((*MockDatastore)(nil).SendSigningRequest), ctx, signingRequestWriter) -} - -// SetOrderPaid mocks base method. -func (m *MockDatastore) SetOrderPaid(arg0 context.Context, arg1 *go_uuid.UUID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetOrderPaid", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetOrderPaid indicates an expected call of SetOrderPaid. -func (mr *MockDatastoreMockRecorder) SetOrderPaid(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetOrderPaid", reflect.TypeOf((*MockDatastore)(nil).SetOrderPaid), arg0, arg1) -} - -// SetOrderTrialDays mocks base method. -func (m *MockDatastore) SetOrderTrialDays(ctx context.Context, orderID *go_uuid.UUID, days int64) (*Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetOrderTrialDays", ctx, orderID, days) - ret0, _ := ret[0].(*Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SetOrderTrialDays indicates an expected call of SetOrderTrialDays. -func (mr *MockDatastoreMockRecorder) SetOrderTrialDays(ctx, orderID, days interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetOrderTrialDays", reflect.TypeOf((*MockDatastore)(nil).SetOrderTrialDays), ctx, orderID, days) -} - -// UpdateOrder mocks base method. -func (m *MockDatastore) UpdateOrder(orderID go_uuid.UUID, status string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateOrder", orderID, status) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateOrder indicates an expected call of UpdateOrder. -func (mr *MockDatastoreMockRecorder) UpdateOrder(orderID, status interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOrder", reflect.TypeOf((*MockDatastore)(nil).UpdateOrder), orderID, status) -} - -// UpdateOrderMetadata mocks base method. -func (m *MockDatastore) UpdateOrderMetadata(orderID go_uuid.UUID, key, value string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateOrderMetadata", orderID, key, value) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateOrderMetadata indicates an expected call of UpdateOrderMetadata. -func (mr *MockDatastoreMockRecorder) UpdateOrderMetadata(orderID, key, value interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOrderMetadata", reflect.TypeOf((*MockDatastore)(nil).UpdateOrderMetadata), orderID, key, value) -} - -// UpdateSigningOrderRequestOutboxTx mocks base method. -func (m *MockDatastore) UpdateSigningOrderRequestOutboxTx(ctx context.Context, tx *sqlx.Tx, requestID go_uuid.UUID, completedAt time.Time) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateSigningOrderRequestOutboxTx", ctx, tx, requestID, completedAt) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateSigningOrderRequestOutboxTx indicates an expected call of UpdateSigningOrderRequestOutboxTx. -func (mr *MockDatastoreMockRecorder) UpdateSigningOrderRequestOutboxTx(ctx, tx, requestID, completedAt interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSigningOrderRequestOutboxTx", reflect.TypeOf((*MockDatastore)(nil).UpdateSigningOrderRequestOutboxTx), ctx, tx, requestID, completedAt) -} - -// UpdateTransaction mocks base method. -func (m *MockDatastore) UpdateTransaction(orderID go_uuid.UUID, externalTransactionID, status, currency, kind string, amount decimal.Decimal) (*Transaction, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateTransaction", orderID, externalTransactionID, status, currency, kind, amount) - ret0, _ := ret[0].(*Transaction) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UpdateTransaction indicates an expected call of UpdateTransaction. -func (mr *MockDatastoreMockRecorder) UpdateTransaction(orderID, externalTransactionID, status, currency, kind, amount interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTransaction", reflect.TypeOf((*MockDatastore)(nil).UpdateTransaction), orderID, externalTransactionID, status, currency, kind, amount) -} - -// MockorderStore is a mock of orderStore interface. -type MockorderStore struct { - ctrl *gomock.Controller - recorder *MockorderStoreMockRecorder -} - -// MockorderStoreMockRecorder is the mock recorder for MockorderStore. -type MockorderStoreMockRecorder struct { - mock *MockorderStore -} - -// NewMockorderStore creates a new mock instance. -func NewMockorderStore(ctrl *gomock.Controller) *MockorderStore { - mock := &MockorderStore{ctrl: ctrl} - mock.recorder = &MockorderStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockorderStore) EXPECT() *MockorderStoreMockRecorder { - return m.recorder -} - -// AppendMetadata mocks base method. -func (m *MockorderStore) AppendMetadata(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, key, val string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendMetadata", ctx, dbi, id, key, val) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendMetadata indicates an expected call of AppendMetadata. -func (mr *MockorderStoreMockRecorder) AppendMetadata(ctx, dbi, id, key, val interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendMetadata", reflect.TypeOf((*MockorderStore)(nil).AppendMetadata), ctx, dbi, id, key, val) -} - -// AppendMetadataInt mocks base method. -func (m *MockorderStore) AppendMetadataInt(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, key string, val int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppendMetadataInt", ctx, dbi, id, key, val) - ret0, _ := ret[0].(error) - return ret0 -} - -// AppendMetadataInt indicates an expected call of AppendMetadataInt. -func (mr *MockorderStoreMockRecorder) AppendMetadataInt(ctx, dbi, id, key, val interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendMetadataInt", reflect.TypeOf((*MockorderStore)(nil).AppendMetadataInt), ctx, dbi, id, key, val) -} - -// Create mocks base method. -func (m *MockorderStore) Create(ctx context.Context, dbi sqlx.QueryerContext, totalPrice decimal.Decimal, merchantID, status, currency, location string, paymentMethods []string, validFor *time.Duration) (*model.Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", ctx, dbi, totalPrice, merchantID, status, currency, location, paymentMethods, validFor) - ret0, _ := ret[0].(*model.Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create. -func (mr *MockorderStoreMockRecorder) Create(ctx, dbi, totalPrice, merchantID, status, currency, location, paymentMethods, validFor interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockorderStore)(nil).Create), ctx, dbi, totalPrice, merchantID, status, currency, location, paymentMethods, validFor) -} - -// Get mocks base method. -func (m *MockorderStore) Get(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (*model.Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", ctx, dbi, id) - ret0, _ := ret[0].(*model.Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get. -func (mr *MockorderStoreMockRecorder) Get(ctx, dbi, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockorderStore)(nil).Get), ctx, dbi, id) -} - -// GetByExternalID mocks base method. -func (m *MockorderStore) GetByExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (*model.Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetByExternalID", ctx, dbi, extID) - ret0, _ := ret[0].(*model.Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetByExternalID indicates an expected call of GetByExternalID. -func (mr *MockorderStoreMockRecorder) GetByExternalID(ctx, dbi, extID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByExternalID", reflect.TypeOf((*MockorderStore)(nil).GetByExternalID), ctx, dbi, extID) -} - -// GetExpiredStripeCheckoutSessionID mocks base method. -func (m *MockorderStore) GetExpiredStripeCheckoutSessionID(ctx context.Context, dbi sqlx.QueryerContext, orderID go_uuid.UUID) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetExpiredStripeCheckoutSessionID", ctx, dbi, orderID) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetExpiredStripeCheckoutSessionID indicates an expected call of GetExpiredStripeCheckoutSessionID. -func (mr *MockorderStoreMockRecorder) GetExpiredStripeCheckoutSessionID(ctx, dbi, orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpiredStripeCheckoutSessionID", reflect.TypeOf((*MockorderStore)(nil).GetExpiredStripeCheckoutSessionID), ctx, dbi, orderID) -} - -// GetExpiresAtAfterISOPeriod mocks base method. -func (m *MockorderStore) GetExpiresAtAfterISOPeriod(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (time.Time, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetExpiresAtAfterISOPeriod", ctx, dbi, id) - ret0, _ := ret[0].(time.Time) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetExpiresAtAfterISOPeriod indicates an expected call of GetExpiresAtAfterISOPeriod. -func (mr *MockorderStoreMockRecorder) GetExpiresAtAfterISOPeriod(ctx, dbi, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpiresAtAfterISOPeriod", reflect.TypeOf((*MockorderStore)(nil).GetExpiresAtAfterISOPeriod), ctx, dbi, id) -} - -// GetMetadata mocks base method. -func (m *MockorderStore) GetMetadata(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (datastore.Metadata, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMetadata", ctx, dbi, id) - ret0, _ := ret[0].(datastore.Metadata) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetMetadata indicates an expected call of GetMetadata. -func (mr *MockorderStoreMockRecorder) GetMetadata(ctx, dbi, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockorderStore)(nil).GetMetadata), ctx, dbi, id) -} - -// HasExternalID mocks base method. -func (m *MockorderStore) HasExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HasExternalID", ctx, dbi, extID) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// HasExternalID indicates an expected call of HasExternalID. -func (mr *MockorderStoreMockRecorder) HasExternalID(ctx, dbi, extID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasExternalID", reflect.TypeOf((*MockorderStore)(nil).HasExternalID), ctx, dbi, extID) -} - -// SetExpiresAt mocks base method. -func (m *MockorderStore) SetExpiresAt(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, when time.Time) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetExpiresAt", ctx, dbi, id, when) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetExpiresAt indicates an expected call of SetExpiresAt. -func (mr *MockorderStoreMockRecorder) SetExpiresAt(ctx, dbi, id, when interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetExpiresAt", reflect.TypeOf((*MockorderStore)(nil).SetExpiresAt), ctx, dbi, id, when) -} - -// SetLastPaidAt mocks base method. -func (m *MockorderStore) SetLastPaidAt(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, when time.Time) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetLastPaidAt", ctx, dbi, id, when) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetLastPaidAt indicates an expected call of SetLastPaidAt. -func (mr *MockorderStoreMockRecorder) SetLastPaidAt(ctx, dbi, id, when interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastPaidAt", reflect.TypeOf((*MockorderStore)(nil).SetLastPaidAt), ctx, dbi, id, when) -} - -// SetStatus mocks base method. -func (m *MockorderStore) SetStatus(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, status string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetStatus", ctx, dbi, id, status) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetStatus indicates an expected call of SetStatus. -func (mr *MockorderStoreMockRecorder) SetStatus(ctx, dbi, id, status interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatus", reflect.TypeOf((*MockorderStore)(nil).SetStatus), ctx, dbi, id, status) -} - -// SetTrialDays mocks base method. -func (m *MockorderStore) SetTrialDays(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID, ndays int64) (*model.Order, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetTrialDays", ctx, dbi, id, ndays) - ret0, _ := ret[0].(*model.Order) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SetTrialDays indicates an expected call of SetTrialDays. -func (mr *MockorderStoreMockRecorder) SetTrialDays(ctx, dbi, id, ndays interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrialDays", reflect.TypeOf((*MockorderStore)(nil).SetTrialDays), ctx, dbi, id, ndays) -} - -// UpdateMetadata mocks base method. -func (m *MockorderStore) UpdateMetadata(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, data datastore.Metadata) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateMetadata", ctx, dbi, id, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateMetadata indicates an expected call of UpdateMetadata. -func (mr *MockorderStoreMockRecorder) UpdateMetadata(ctx, dbi, id, data interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMetadata", reflect.TypeOf((*MockorderStore)(nil).UpdateMetadata), ctx, dbi, id, data) -} - -// MockorderItemStore is a mock of orderItemStore interface. -type MockorderItemStore struct { - ctrl *gomock.Controller - recorder *MockorderItemStoreMockRecorder -} - -// MockorderItemStoreMockRecorder is the mock recorder for MockorderItemStore. -type MockorderItemStoreMockRecorder struct { - mock *MockorderItemStore -} - -// NewMockorderItemStore creates a new mock instance. -func NewMockorderItemStore(ctrl *gomock.Controller) *MockorderItemStore { - mock := &MockorderItemStore{ctrl: ctrl} - mock.recorder = &MockorderItemStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockorderItemStore) EXPECT() *MockorderItemStoreMockRecorder { - return m.recorder -} - -// FindByOrderID mocks base method. -func (m *MockorderItemStore) FindByOrderID(ctx context.Context, dbi sqlx.QueryerContext, orderID go_uuid.UUID) ([]model.OrderItem, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindByOrderID", ctx, dbi, orderID) - ret0, _ := ret[0].([]model.OrderItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindByOrderID indicates an expected call of FindByOrderID. -func (mr *MockorderItemStoreMockRecorder) FindByOrderID(ctx, dbi, orderID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByOrderID", reflect.TypeOf((*MockorderItemStore)(nil).FindByOrderID), ctx, dbi, orderID) -} - -// Get mocks base method. -func (m *MockorderItemStore) Get(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (*model.OrderItem, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", ctx, dbi, id) - ret0, _ := ret[0].(*model.OrderItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get. -func (mr *MockorderItemStoreMockRecorder) Get(ctx, dbi, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockorderItemStore)(nil).Get), ctx, dbi, id) -} - -// InsertMany mocks base method. -func (m *MockorderItemStore) InsertMany(ctx context.Context, dbi sqlx.ExtContext, items ...model.OrderItem) ([]model.OrderItem, error) { - m.ctrl.T.Helper() - varargs := []interface{}{ctx, dbi} - for _, a := range items { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "InsertMany", varargs...) - ret0, _ := ret[0].([]model.OrderItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// InsertMany indicates an expected call of InsertMany. -func (mr *MockorderItemStoreMockRecorder) InsertMany(ctx, dbi interface{}, items ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, dbi}, items...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertMany", reflect.TypeOf((*MockorderItemStore)(nil).InsertMany), varargs...) -} - -// MockorderPayHistoryStore is a mock of orderPayHistoryStore interface. -type MockorderPayHistoryStore struct { - ctrl *gomock.Controller - recorder *MockorderPayHistoryStoreMockRecorder -} - -// MockorderPayHistoryStoreMockRecorder is the mock recorder for MockorderPayHistoryStore. -type MockorderPayHistoryStoreMockRecorder struct { - mock *MockorderPayHistoryStore -} - -// NewMockorderPayHistoryStore creates a new mock instance. -func NewMockorderPayHistoryStore(ctrl *gomock.Controller) *MockorderPayHistoryStore { - mock := &MockorderPayHistoryStore{ctrl: ctrl} - mock.recorder = &MockorderPayHistoryStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockorderPayHistoryStore) EXPECT() *MockorderPayHistoryStoreMockRecorder { - return m.recorder -} - -// Insert mocks base method. -func (m *MockorderPayHistoryStore) Insert(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, when time.Time) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, dbi, id, when) - ret0, _ := ret[0].(error) - return ret0 -} - -// Insert indicates an expected call of Insert. -func (mr *MockorderPayHistoryStoreMockRecorder) Insert(ctx, dbi, id, when interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockorderPayHistoryStore)(nil).Insert), ctx, dbi, id, when) -} diff --git a/services/skus/model/model.go b/services/skus/model/model.go deleted file mode 100644 index 83bd63f72..000000000 --- a/services/skus/model/model.go +++ /dev/null @@ -1,645 +0,0 @@ -// Package model provides data that the SKUs service operates on. -package model - -import ( - "context" - "database/sql" - "fmt" - "net/url" - "sort" - "strconv" - "time" - - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stripe/stripe-go/v72" - "github.com/stripe/stripe-go/v72/checkout/session" - "github.com/stripe/stripe-go/v72/customer" - - "github.com/brave-intl/bat-go/libs/clients/radom" - "github.com/brave-intl/bat-go/libs/datastore" -) - -const ( - ErrOrderNotFound Error = "model: order not found" - ErrOrderItemNotFound Error = "model: order item not found" - ErrIssuerNotFound Error = "model: issuer not found" - ErrNoRowsChangedOrder Error = "model: no rows changed in orders" - ErrNoRowsChangedOrderPayHistory Error = "model: no rows changed in order_payment_history" - ErrExpiredStripeCheckoutSessionIDNotFound Error = "model: expired stripeCheckoutSessionId not found" - ErrInvalidOrderNoItems Error = "model: invalid order: no items" - ErrInvalidOrderNoSuccessURL Error = "model: invalid order: no success url" - ErrInvalidOrderNoCancelURL Error = "model: invalid order: no cancel url" - ErrInvalidOrderNoProductID Error = "model: invalid order: no product id" - - ErrNumPerIntervalNotSet Error = "model: invalid order: numPerInterval must be set" - ErrNumIntervalsNotSet Error = "model: invalid order: numIntervals must be set" - ErrInvalidNumPerInterval Error = "model: invalid order: invalid numPerInterval" - ErrInvalidNumIntervals Error = "model: invalid order: invalid numIntervals" - - // The text of the following errors is preserved as is, in case anything depends on them. - ErrInvalidSKU Error = "Invalid SKU Token provided in request" - ErrDifferentPaymentMethods Error = "all order items must have the same allowed payment methods" - ErrInvalidOrderRequest Error = "model: no items to be created" - - errInvalidNumConversion Error = "model: invalid numeric conversion" -) - -const ( - StripePaymentMethod = "stripe" - RadomPaymentMethod = "radom" - - // OrderStatus* represent order statuses at runtime and in db. - OrderStatusCanceled = "canceled" - OrderStatusPaid = "paid" - OrderStatusPending = "pending" - - issuerBufferDefault = 30 - issuerOverlapDefault = 5 -) - -var ( - emptyCreateCheckoutSessionResp CreateCheckoutSessionResponse - emptyOrderTimeBounds OrderTimeBounds -) - -type radomClient interface { - CreateCheckoutSession(ctx context.Context, req *radom.CheckoutSessionRequest) (*radom.CheckoutSessionResponse, error) -} - -// Order represents an individual order. -type Order struct { - ID uuid.UUID `json:"id" db:"id"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - Currency string `json:"currency" db:"currency"` - UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` - TotalPrice decimal.Decimal `json:"totalPrice" db:"total_price"` - MerchantID string `json:"merchantId" db:"merchant_id"` - Location datastore.NullString `json:"location" db:"location"` - Status string `json:"status" db:"status"` - Items []OrderItem `json:"items"` - AllowedPaymentMethods pq.StringArray `json:"allowedPaymentMethods" db:"allowed_payment_methods"` - Metadata datastore.Metadata `json:"metadata" db:"metadata"` - LastPaidAt *time.Time `json:"lastPaidAt" db:"last_paid_at"` - ExpiresAt *time.Time `json:"expiresAt" db:"expires_at"` - ValidFor *time.Duration `json:"validFor" db:"valid_for"` - TrialDays *int64 `json:"-" db:"trial_days"` -} - -// IsStripePayable returns true if every item is payable by Stripe. -func (o *Order) IsStripePayable() bool { - // TODO: if not we need to look into subscription trials: - // -> https://stripe.com/docs/billing/subscriptions/trials - - return Slice[string](o.AllowedPaymentMethods).Contains(StripePaymentMethod) -} - -// IsRadomPayable indicates whether the order is payable by Radom. -func (o *Order) IsRadomPayable() bool { - return Slice[string](o.AllowedPaymentMethods).Contains(RadomPaymentMethod) -} - -// CreateStripeCheckoutSession creates a Stripe checkout session for the order. -// -// Deprecated: Use CreateStripeCheckoutSession function instead of this method. -func (o *Order) CreateStripeCheckoutSession( - email, successURI, cancelURI string, - freeTrialDays int64, -) (CreateCheckoutSessionResponse, error) { - return CreateStripeCheckoutSession(o.ID.String(), email, successURI, cancelURI, freeTrialDays, o.Items) -} - -// CreateStripeCheckoutSession creates a Stripe checkout session for the order. -func CreateStripeCheckoutSession( - oid, email, successURI, cancelURI string, - trialDays int64, - items []OrderItem, -) (CreateCheckoutSessionResponse, error) { - var custID string - if email != "" { - // Find the existing customer by email to use the customer id instead email. - l := customer.List(&stripe.CustomerListParams{ - Email: stripe.String(email), - }) - - for l.Next() { - custID = l.Customer().ID - } - } - - params := &stripe.CheckoutSessionParams{ - PaymentMethodTypes: stripe.StringSlice([]string{"card"}), - Mode: stripe.String(string(stripe.CheckoutSessionModeSubscription)), - SuccessURL: stripe.String(successURI), - CancelURL: stripe.String(cancelURI), - ClientReferenceID: stripe.String(oid), - SubscriptionData: &stripe.CheckoutSessionSubscriptionDataParams{}, - LineItems: OrderItemList(items).stripeLineItems(), - } - - // If a free trial is set, apply it. - if trialDays > 0 { - params.SubscriptionData.TrialPeriodDays = &trialDays - } - - if custID != "" { - // Use existing customer if found. - params.Customer = stripe.String(custID) - } else if email != "" { - // Otherwise, create a new using email. - params.CustomerEmail = stripe.String(email) - } - // Otherwise, we have no record of this email for this checkout session. - // ? The user will be asked for the email, we cannot send an empty customer email as a param. - - params.SubscriptionData.AddMetadata("orderID", oid) - params.AddExtra("allow_promotion_codes", "true") - - session, err := session.New(params) - if err != nil { - return EmptyCreateCheckoutSessionResponse(), fmt.Errorf("failed to create stripe session: %w", err) - } - - return CreateCheckoutSessionResponse{SessionID: session.ID}, nil -} - -// CreateRadomCheckoutSession creates a Radom checkout session for o. -func (o *Order) CreateRadomCheckoutSession( - ctx context.Context, - client radomClient, - sellerAddr string, -) (CreateCheckoutSessionResponse, error) { - return o.CreateRadomCheckoutSessionWithTime(ctx, client, sellerAddr, time.Now().Add(24*time.Hour)) -} - -// CreateRadomCheckoutSessionWithTime creates a Radom checkout session for o. -// -// TODO: This must be refactored before it's usable. Issues with the current implementation: -// - it assumes one item per order; -// - most of the logic does not belong in here; -// - metadata information must be passed explisictly instead of being parsed (it's known prior to this place); -// And more. -func (o *Order) CreateRadomCheckoutSessionWithTime( - ctx context.Context, - client radomClient, - sellerAddr string, - expiresAt time.Time, -) (CreateCheckoutSessionResponse, error) { - if len(o.Items) < 1 { - return EmptyCreateCheckoutSessionResponse(), ErrInvalidOrderNoItems - } - - successURI, ok := o.Items[0].Metadata["radom_success_uri"].(string) - if !ok { - return EmptyCreateCheckoutSessionResponse(), ErrInvalidOrderNoSuccessURL - } - - cancelURI, ok := o.Items[0].Metadata["radom_cancel_uri"].(string) - if !ok { - return EmptyCreateCheckoutSessionResponse(), ErrInvalidOrderNoCancelURL - } - - productID, ok := o.Items[0].Metadata["radom_product_id"].(string) - if !ok { - return EmptyCreateCheckoutSessionResponse(), ErrInvalidOrderNoProductID - } - - resp, err := client.CreateCheckoutSession(ctx, &radom.CheckoutSessionRequest{ - SuccessURL: successURI, - CancelURL: cancelURI, - // Gateway will be set by the client. - Metadata: radom.Metadata([]radom.KeyValue{ - { - Key: "braveOrderId", - Value: o.ID.String(), - }, - }), - LineItems: []radom.LineItem{ - { - ProductID: productID, - }, - }, - ExpiresAt: expiresAt.Unix(), - Customizations: map[string]interface{}{ - "leftPanelColor": "linear-gradient(125deg, rgba(0,0,128,1) 0%, RGBA(196,22,196,1) 100%)", - "primaryButtonColor": "#000000", - "slantedEdge": true, - }, - }) - if err != nil { - return EmptyCreateCheckoutSessionResponse(), fmt.Errorf("failed to get checkout session response: %w", err) - } - - return CreateCheckoutSessionResponse{SessionID: resp.SessionID}, nil -} - -// IsPaid returns true if the order is paid. -func (o *Order) IsPaid() bool { - switch o.Status { - case OrderStatusPaid: - // The order is paid if the status is paid. - return true - case OrderStatusCanceled: - // Check to make sure that expires_a is after now, if order is cancelled. - if o.ExpiresAt == nil { - return false - } - - return o.ExpiresAt.After(time.Now()) - default: - return false - } -} - -func (o *Order) GetTrialDays() int64 { - if o.TrialDays == nil { - return 0 - } - - return *o.TrialDays -} - -func (o *Order) NumPerInterval() (int, error) { - numRaw, ok := o.Metadata["numPerInterval"] - if !ok { - return 0, ErrNumPerIntervalNotSet - } - - result, err := numFromAny(numRaw) - if err != nil { - return 0, ErrInvalidNumPerInterval - } - - return result, nil -} - -func (o *Order) NumIntervals() (int, error) { - numRaw, ok := o.Metadata["numIntervals"] - if !ok { - return 0, ErrNumIntervalsNotSet - } - - result, err := numFromAny(numRaw) - if err != nil { - return 0, ErrInvalidNumIntervals - } - - return result, nil -} - -// HasItem returns the item if found. -// -// It exposes a comma, ok API similar to a map. -// Today items are stored in a slice, but it might change to a map in the future. -func (o *Order) HasItem(id uuid.UUID) (*OrderItem, bool) { - for i := range o.Items { - if uuid.Equal(o.Items[i].ID, id) { - return &o.Items[i], true - } - } - - return nil, false -} - -// OrderItem represents a particular order item. -type OrderItem struct { - ID uuid.UUID `json:"id" db:"id"` - OrderID uuid.UUID `json:"orderId" db:"order_id"` - SKU string `json:"sku" db:"sku"` - CreatedAt *time.Time `json:"createdAt" db:"created_at"` - UpdatedAt *time.Time `json:"updatedAt" db:"updated_at"` - Currency string `json:"currency" db:"currency"` - Quantity int `json:"quantity" db:"quantity"` - Price decimal.Decimal `json:"price" db:"price"` - Subtotal decimal.Decimal `json:"subtotal" db:"subtotal"` - Location datastore.NullString `json:"location" db:"location"` - Description datastore.NullString `json:"description" db:"description"` - CredentialType string `json:"credentialType" db:"credential_type"` - ValidFor *time.Duration `json:"validFor" db:"valid_for"` - ValidForISO *string `json:"validForIso" db:"valid_for_iso"` - EachCredentialValidForISO *string `json:"-" db:"each_credential_valid_for_iso"` - Metadata datastore.Metadata `json:"metadata" db:"metadata"` - IssuanceIntervalISO *string `json:"issuanceInterval" db:"issuance_interval"` - - // TODO: Remove this when products & issuers have been reworked. - // The issuer for a product must be created when the product is created. - IssuerConfig *IssuerConfig `json:"-" db:"-"` -} - -func (x *OrderItem) IsLeo() bool { - if x == nil { - return false - } - - return x.SKU == "brave-leo-premium" -} - -// OrderNew represents a request to create an order in the database. -type OrderNew struct { - MerchantID string `db:"merchant_id"` - Currency string `db:"currency"` - Status string `db:"status"` - Location sql.NullString `db:"location"` - TotalPrice decimal.Decimal `db:"total_price"` - AllowedPaymentMethods pq.StringArray `db:"allowed_payment_methods"` - ValidFor *time.Duration `db:"valid_for"` -} - -// CreateCheckoutSessionResponse represents a checkout session response. -type CreateCheckoutSessionResponse struct { - SessionID string `json:"checkoutSessionId"` -} - -func EmptyCreateCheckoutSessionResponse() CreateCheckoutSessionResponse { - return emptyCreateCheckoutSessionResp -} - -type OrderItemList []OrderItem - -func (l OrderItemList) SetOrderID(orderID uuid.UUID) { - for i := range l { - l[i].OrderID = orderID - } -} - -func (l OrderItemList) TotalCost() decimal.Decimal { - var result decimal.Decimal - - for i := range l { - result = result.Add(l[i].Subtotal) - } - - return result -} - -func (l OrderItemList) stripeLineItems() []*stripe.CheckoutSessionLineItemParams { - result := make([]*stripe.CheckoutSessionLineItemParams, 0, len(l)) - - for _, item := range l { - // Obtain the item id from the metadata. - priceID, ok := item.Metadata["stripe_item_id"].(string) - if !ok { - continue - } - - // Assume that the stripe product is embedded in macaroon as metadata - // because a stripe line item is being created. - result = append(result, &stripe.CheckoutSessionLineItemParams{ - Price: stripe.String(priceID), - Quantity: stripe.Int64(int64(item.Quantity)), - }) - } - - return result -} - -type Error string - -func (e Error) Error() string { - return string(e) -} - -type OrderTimeBounds struct { - ValidFor *time.Duration `db:"valid_for"` - LastPaid sql.NullTime `db:"last_paid_at"` -} - -func EmptyOrderTimeBounds() OrderTimeBounds { - return emptyOrderTimeBounds -} - -// ExpiresAt computes expiry time, and uses now if last paid was not set before. -func (x *OrderTimeBounds) ExpiresAt() time.Time { - // Default to last paid now. - return x.ExpiresAtWithFallback(time.Now()) -} - -// ExpiresAtWithFallback computes expiry time, and uses fallback for last paid, if it was not set before. -func (x *OrderTimeBounds) ExpiresAtWithFallback(fallback time.Time) time.Time { - // Default to fallback. - // Use valid last paid from order, if available. - lastPaid := fallback - if x.LastPaid.Valid { - lastPaid = x.LastPaid.Time - } - - var expiresAt time.Time - if x.ValidFor != nil { - // Compute expiry based on valid for. - expiresAt = lastPaid.Add(*x.ValidFor) - } - - return expiresAt -} - -// CreateOrderRequest includes information needed to create an order. -type CreateOrderRequest struct { - Email string `json:"email" valid:"-"` - Items []OrderItemRequest `json:"items" valid:"-"` -} - -// OrderItemRequest represents an item in a order request. -type OrderItemRequest struct { - SKU string `json:"sku" valid:"-"` - Quantity int `json:"quantity" valid:"int"` -} - -// CreateOrderRequestNew includes information needed to create an order. -type CreateOrderRequestNew struct { - Email string `json:"email" validate:"required,email"` - Currency string `json:"currency" validate:"required,iso4217"` - StripeMetadata *OrderStripeMetadata `json:"stripe_metadata"` - PaymentMethods []string `json:"payment_methods" validate:"required,gt=0"` - Items []OrderItemRequestNew `json:"items" validate:"required,gt=0,dive"` -} - -// OrderItemRequestNew represents an item in an order request. -type OrderItemRequestNew struct { - Quantity int `json:"quantity" validate:"required,gte=1"` - IssuerTokenBuffer int `json:"issuer_token_buffer"` - IssuerTokenOverlap int `json:"issuer_token_overlap"` - SKU string `json:"sku" validate:"required"` - Location string `json:"location" validate:"required"` - Description string `json:"description" validate:"required"` - CredentialType string `json:"credential_type" validate:"required"` - CredentialValidDuration string `json:"credential_valid_duration" validate:"required"` - Price decimal.Decimal `json:"price"` - CredentialValidDurationEach *string `json:"each_credential_valid_duration"` - IssuanceInterval *string `json:"issuance_interval"` - StripeMetadata *ItemStripeMetadata `json:"stripe_metadata"` -} - -func (r *OrderItemRequestNew) TokenBufferOrDefault() int { - if r == nil { - return 0 - } - - if r.IssuerTokenBuffer == 0 { - return issuerBufferDefault - } - - return r.IssuerTokenBuffer -} - -func (r *OrderItemRequestNew) TokenOverlapOrDefault() int { - if r == nil { - return 0 - } - - if r.IssuerTokenOverlap == 0 { - return issuerOverlapDefault - } - - return r.IssuerTokenOverlap -} - -// OrderStripeMetadata holds data relevant to the order in Stripe. -type OrderStripeMetadata struct { - SuccessURI string `json:"success_uri" validate:"http_url"` - CancelURI string `json:"cancel_uri" validate:"http_url"` -} - -func (m *OrderStripeMetadata) SuccessURL(oid string) (string, error) { - if m == nil { - return "", nil - } - - return addURLParam(m.SuccessURI, "order_id", oid) -} - -func (m *OrderStripeMetadata) CancelURL(oid string) (string, error) { - if m == nil { - return "", nil - } - - return addURLParam(m.CancelURI, "order_id", oid) -} - -// ItemStripeMetadata holds data about the product in Stripe. -type ItemStripeMetadata struct { - ProductID string `json:"product_id"` - ItemID string `json:"item_id"` -} - -// Metadata returns the contents of m as a map for datastore.Metadata. -// -// It can be called when m is nil. -func (m *ItemStripeMetadata) Metadata() map[string]interface{} { - if m == nil { - return nil - } - - result := make(map[string]interface{}) - if m.ProductID != "" { - result["stripe_product_id"] = m.ProductID - } - - if m.ItemID != "" { - result["stripe_item_id"] = m.ItemID - } - - return result -} - -// EnsureEqualPaymentMethods checks if the methods list equals the incoming list. -// -// This operation may change both slices due to sorting. -func EnsureEqualPaymentMethods(methods, incoming []string) error { - sort.Strings(methods) - sort.Strings(incoming) - - if !Slice[string](methods).Equal(Slice[string](incoming)) { - return ErrDifferentPaymentMethods - } - - return nil -} - -type Slice[T comparable] []T - -func (s Slice[T]) Equal(target []T) bool { - if len(s) != len(target) { - return false - } - - for i, v := range s { - if v != target[i] { - return false - } - } - - return true -} - -func (s Slice[T]) Contains(target T) bool { - for _, v := range s { - if v == target { - return true - } - } - - return false -} - -// Issuer represents a credential issuer. -type Issuer struct { - ID uuid.UUID `json:"id" db:"id"` - MerchantID string `json:"merchantId" db:"merchant_id"` - PublicKey string `json:"publicKey" db:"public_key"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` -} - -// Name returns the name of the issuer as known by the challenge bypass server. -func (x *Issuer) Name() string { - return x.MerchantID -} - -// IssuerNew is a request to create an issuer in the database. -type IssuerNew struct { - MerchantID string `db:"merchant_id"` - PublicKey string `db:"public_key"` -} - -// IssuerConfig holds configuration of an issuer. -type IssuerConfig struct { - Buffer int - Overlap int -} - -func (c *IssuerConfig) NumIntervals() int { - return c.Buffer + c.Overlap -} - -func addURLParam(src, name, val string) (string, error) { - raw, err := url.Parse(src) - if err != nil { - return "", err - } - - v := raw.Query() - v.Add(name, val) - - raw.RawQuery = v.Encode() - - return raw.String(), nil -} - -func numFromAny(raw any) (int, error) { - switch v := raw.(type) { - case int: - return v, nil - case int64: - return int(v), nil - case int32: - return int(v), nil - case float32: - return int(v), nil - case float64: - return int(v), nil - case string: - return strconv.Atoi(v) - default: - return 0, errInvalidNumConversion - } -} diff --git a/services/skus/model/model_pvt_test.go b/services/skus/model/model_pvt_test.go deleted file mode 100644 index 84bb72e23..000000000 --- a/services/skus/model/model_pvt_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package model - -import ( - "errors" - "testing" - - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" -) - -func TestAddURLParam(t *testing.T) { - type tcGiven struct { - src string - name string - val string - } - - type tcExpected struct { - result string - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - // Don't test for invalid inputs due to url.Parse's tolerance. - tests := []testCase{ - { - name: "empty", - exp: tcExpected{result: "?="}, - }, - - { - name: "add_nothing", - given: tcGiven{ - src: "https://example.com", - }, - exp: tcExpected{ - result: "https://example.com?=", - }, - }, - - { - name: "add_one", - given: tcGiven{ - src: "https://example.com", - name: "param1", - val: "val1", - }, - exp: tcExpected{ - result: "https://example.com?param1=val1", - }, - }, - - { - name: "add_second", - given: tcGiven{ - src: "https://example.com?param=val", - name: "param2", - val: "val2", - }, - exp: tcExpected{ - result: "https://example.com?param=val¶m2=val2", - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act, err := addURLParam(tc.given.src, tc.given.name, tc.given.val) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - should.Equal(t, tc.exp.result, act) - }) - } -} diff --git a/services/skus/model/model_test.go b/services/skus/model/model_test.go deleted file mode 100644 index 237119485..000000000 --- a/services/skus/model/model_test.go +++ /dev/null @@ -1,712 +0,0 @@ -package model_test - -import ( - "context" - "encoding/json" - "errors" - "net" - "testing" - "time" - - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/libs/clients/radom" - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/services/skus/model" -) - -func TestOrder_IsStripePayable(t *testing.T) { - type testCase struct { - name string - given model.Order - exp bool - } - - tests := []testCase{ - { - name: "empty", - }, - - { - name: "something_else", - given: model.Order{AllowedPaymentMethods: pq.StringArray{"something_else"}}, - }, - - { - name: "stripe_only", - given: model.Order{AllowedPaymentMethods: pq.StringArray{"stripe"}}, - exp: true, - }, - - { - name: "something_else_stripe", - given: model.Order{AllowedPaymentMethods: pq.StringArray{"something_else", "stripe"}}, - exp: true, - }, - - { - name: "stripe_something_else", - given: model.Order{AllowedPaymentMethods: pq.StringArray{"stripe", "something_else"}}, - exp: true, - }, - - { - name: "more_stripe_something_else", - given: model.Order{AllowedPaymentMethods: pq.StringArray{"more", "stripe", "something_else"}}, - exp: true, - }, - - { - name: "mixed", - given: model.Order{AllowedPaymentMethods: pq.StringArray{"more", "stripe", "something_else", "stripe"}}, - exp: true, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act := tc.given.IsStripePayable() - should.Equal(t, tc.exp, act) - }) - } -} - -func TestEnsureEqualPaymentMethods(t *testing.T) { - type tcGiven struct { - a []string - b []string - } - - type testCase struct { - name string - given tcGiven - exp error - } - - tests := []testCase{ - { - name: "empty", - }, - - { - name: "stripe_empty", - given: tcGiven{ - a: []string{"stripe"}, - }, - exp: model.ErrDifferentPaymentMethods, - }, - - { - name: "stripe_something", - given: tcGiven{ - a: []string{"stripe"}, - b: []string{"something"}, - }, - exp: model.ErrDifferentPaymentMethods, - }, - - { - name: "equal_single", - given: tcGiven{ - a: []string{"stripe"}, - b: []string{"stripe"}, - }, - }, - - { - name: "equal_sorting", - given: tcGiven{ - a: []string{"cash", "stripe"}, - b: []string{"stripe", "cash"}, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act := model.EnsureEqualPaymentMethods(tc.given.a, tc.given.b) - should.Equal(t, true, errors.Is(tc.exp, act)) - }) - } -} - -func TestOrder_CreateRadomCheckoutSessionWithTime(t *testing.T) { - type tcGiven struct { - order *model.Order - client *radom.MockClient - saddr string - expiresAt time.Time - } - type tcExpected struct { - val model.CreateCheckoutSessionResponse - err error - } - type testCase struct { - name string - given tcGiven - exp tcExpected - } - tests := []testCase{ - { - name: "no_items", - given: tcGiven{ - order: &model.Order{}, - client: &radom.MockClient{}, - }, - exp: tcExpected{ - err: model.ErrInvalidOrderNoItems, - }, - }, - - { - name: "no_radom_success_uri", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{{}}, - }, - client: &radom.MockClient{}, - }, - exp: tcExpected{ - err: model.ErrInvalidOrderNoSuccessURL, - }, - }, - - { - name: "no_radom_cancel_uri", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - Metadata: datastore.Metadata{ - "radom_success_uri": "something", - }, - }, - }, - }, - client: &radom.MockClient{}, - }, - exp: tcExpected{ - err: model.ErrInvalidOrderNoCancelURL, - }, - }, - - { - name: "no_radom_product_id", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - Metadata: datastore.Metadata{ - "radom_success_uri": "something_success", - "radom_cancel_uri": "something_cancel", - }, - }, - }, - }, - client: &radom.MockClient{}, - }, - exp: tcExpected{ - err: model.ErrInvalidOrderNoProductID, - }, - }, - - { - name: "client_error", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - Metadata: datastore.Metadata{ - "radom_success_uri": "something_success", - "radom_cancel_uri": "something_cancel", - "radom_product_id": "something_id", - }, - }, - }, - }, - client: &radom.MockClient{ - FnCreateCheckoutSession: func(ctx context.Context, req *radom.CheckoutSessionRequest) (*radom.CheckoutSessionResponse, error) { - return nil, net.ErrClosed - }, - }, - }, - exp: tcExpected{ - err: net.ErrClosed, - }, - }, - - { - name: "client_success", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - Metadata: datastore.Metadata{ - "radom_success_uri": "something_success", - "radom_cancel_uri": "something_cancel", - "radom_product_id": "something_id", - }, - }, - }, - }, - client: &radom.MockClient{ - FnCreateCheckoutSession: func(ctx context.Context, req *radom.CheckoutSessionRequest) (*radom.CheckoutSessionResponse, error) { - result := &radom.CheckoutSessionResponse{ - SessionID: "session_id", - SessionURL: "session_url", - } - - return result, nil - }, - }, - }, - exp: tcExpected{ - val: model.CreateCheckoutSessionResponse{ - SessionID: "session_id", - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - act, err := tc.given.order.CreateRadomCheckoutSessionWithTime( - ctx, - tc.given.client, - tc.given.saddr, - tc.given.expiresAt, - ) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - should.Equal(t, tc.exp.val, act) - }) - } -} - -func TestOrderItemRequestNew_Unmarshal(t *testing.T) { - type testCase struct { - name string - given []byte - exp *model.OrderItemRequestNew - } - - tests := []testCase{ - { - name: "empty_input", - given: []byte(`{}`), - exp: &model.OrderItemRequestNew{}, - }, - - { - name: "price_string", - given: []byte(`{ - "price": "1" - }`), - exp: &model.OrderItemRequestNew{ - Price: mustDecimalFromString("1"), - }, - }, - - { - name: "price_int", - given: []byte(`{ - "price": 1 - }`), - exp: &model.OrderItemRequestNew{ - Price: decimal.NewFromInt(1), - }, - }, - - { - name: "each_credential_valid_duration", - given: []byte(`{ - "each_credential_valid_duration": "P1D" - }`), - exp: &model.OrderItemRequestNew{ - CredentialValidDurationEach: ptrTo("P1D"), - }, - }, - - { - name: "issuance_interval", - given: []byte(`{ - "issuance_interval": "P1M" - }`), - exp: &model.OrderItemRequestNew{ - IssuanceInterval: ptrTo("P1M"), - }, - }, - - { - name: "stripe_metadata", - given: []byte(`{ - "stripe_metadata": { - "product_id": "product_id", - "item_id": "item_id" - } - }`), - exp: &model.OrderItemRequestNew{ - StripeMetadata: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - }, - }, - - { - name: "optional_fields_together", - given: []byte(`{ - "price": "1", - "each_credential_valid_duration": "P1D", - "issuance_interval": "P1M", - "stripe_metadata": { - "product_id": "product_id", - "item_id": "item_id" - } - }`), - exp: &model.OrderItemRequestNew{ - Price: mustDecimalFromString("1"), - CredentialValidDurationEach: ptrTo("P1D"), - IssuanceInterval: ptrTo("P1M"), - StripeMetadata: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act := &model.OrderItemRequestNew{} - - err := json.Unmarshal(tc.given, act) - must.Equal(t, nil, err) - - should.Equal(t, tc.exp, act) - }) - } -} - -func TestItemStripeMetadata_Metadata(t *testing.T) { - type testCase struct { - name string - given *model.ItemStripeMetadata - exp map[string]interface{} - } - - tests := []testCase{ - { - name: "nil", - }, - - { - name: "empty", - given: &model.ItemStripeMetadata{}, - exp: map[string]interface{}{}, - }, - - { - name: "product_id", - given: &model.ItemStripeMetadata{ - ProductID: "product_id", - }, - exp: map[string]interface{}{ - "stripe_product_id": "product_id", - }, - }, - - { - name: "item_id", - given: &model.ItemStripeMetadata{ - ItemID: "item_id", - }, - exp: map[string]interface{}{ - "stripe_item_id": "item_id", - }, - }, - - { - name: "everything", - given: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - exp: map[string]interface{}{ - "stripe_product_id": "product_id", - "stripe_item_id": "item_id", - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act := tc.given.Metadata() - should.Equal(t, tc.exp, act) - }) - } -} - -func TestOrderStripeMetadata(t *testing.T) { - type tcGiven struct { - data *model.OrderStripeMetadata - oid string - } - - type tcExpected struct { - surl string - curl string - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "empty", - }, - - { - name: "add_id", - given: tcGiven{ - data: &model.OrderStripeMetadata{ - SuccessURI: "https://example.com/success", - CancelURI: "https://example.com/cancel", - }, - oid: "some_order_id", - }, - exp: tcExpected{ - surl: "https://example.com/success?order_id=some_order_id", - curl: "https://example.com/cancel?order_id=some_order_id", - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act1, err := tc.given.data.SuccessURL(tc.given.oid) - must.Equal(t, nil, err) - - should.Equal(t, tc.exp.surl, act1) - - act2, err := tc.given.data.CancelURL(tc.given.oid) - must.Equal(t, nil, err) - - should.Equal(t, tc.exp.curl, act2) - }) - } -} - -func TestOrderItemList_TotalCost(t *testing.T) { - type testCase struct { - name string - given []model.OrderItem - exp decimal.Decimal - } - - tests := []testCase{ - { - name: "empty_zero", - }, - - { - name: "single_zero", - given: []model.OrderItem{ - {}, - }, - }, - - { - name: "single_nonzero", - given: []model.OrderItem{ - {Subtotal: decimal.NewFromInt(10)}, - }, - exp: decimal.NewFromInt(10), - }, - - { - name: "many_zero_nonzero", - given: []model.OrderItem{ - {}, - {Subtotal: decimal.NewFromInt(10)}, - }, - exp: decimal.NewFromInt(10), - }, - - { - name: "many_nonzero", - given: []model.OrderItem{ - {Subtotal: decimal.NewFromInt(11)}, - {Subtotal: decimal.NewFromInt(10)}, - }, - exp: decimal.NewFromInt(21), - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act := model.OrderItemList(tc.given).TotalCost() - should.Equal(t, true, tc.exp.Equal(act)) - }) - } -} - -func TestOrder_HasItem(t *testing.T) { - type tcGiven struct { - order *model.Order - itemID uuid.UUID - } - - type tcExpected struct { - item *model.OrderItem - ok bool - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "no_items_nothing_found", - given: tcGiven{ - order: &model.Order{}, - itemID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - }, - - { - name: "one_item_not_found", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - ID: uuid.Must(uuid.FromString("dbc6416a-7713-4aa5-8968-56aef7ec0e81")), - }, - }, - }, - itemID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - }, - - { - name: "two_items_not_found", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - ID: uuid.Must(uuid.FromString("dbc6416a-7713-4aa5-8968-56aef7ec0e81")), - }, - - { - ID: uuid.Must(uuid.FromString("4efbedfe-a598-43a4-a345-17653d6289e8")), - }, - }, - }, - itemID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - }, - - { - name: "one_item_found", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - ID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - }, - }, - itemID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - exp: tcExpected{ - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - ok: true, - }, - }, - - { - name: "many_items_found", - given: tcGiven{ - order: &model.Order{ - Items: []model.OrderItem{ - { - ID: uuid.Must(uuid.FromString("dbc6416a-7713-4aa5-8968-56aef7ec0e81")), - }, - - { - ID: uuid.Must(uuid.FromString("4efbedfe-a598-43a4-a345-17653d6289e8")), - }, - - { - ID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - }, - }, - itemID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - exp: tcExpected{ - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("b5e3f3e4-0bd4-4fd5-a693-a50f4dbfd6ac")), - }, - ok: true, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - item, ok := tc.given.order.HasItem(tc.given.itemID) - must.Equal(t, tc.exp.ok, ok) - - if !tc.exp.ok { - return - } - - should.Equal(t, tc.exp.item, item) - }) - } -} - -func mustDecimalFromString(v string) decimal.Decimal { - result, err := decimal.NewFromString(v) - if err != nil { - panic(err) - } - - return result -} - -func ptrTo[T any](v T) *T { - return &v -} diff --git a/services/skus/order.go b/services/skus/order.go deleted file mode 100644 index 2b60ede03..000000000 --- a/services/skus/order.go +++ /dev/null @@ -1,202 +0,0 @@ -package skus - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/logging" - timeutils "github.com/brave-intl/bat-go/libs/time" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stripe/stripe-go/v72" - "gopkg.in/macaroon.v2" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -const ( - paymentProcessor = "paymentProcessor" - // IOSPaymentMethod - indicating this used an ios payment method - IOSPaymentMethod = "ios" - // AndroidPaymentMethod - indicating this used an android payment method - AndroidPaymentMethod = "android" -) - -const ( - // TODO(pavelb): Gradually replace it everywhere. - StripePaymentMethod = model.StripePaymentMethod - - StripeInvoiceUpdated = "invoice.updated" - StripeInvoicePaid = "invoice.paid" - StripeCustomerSubscriptionDeleted = "customer.subscription.deleted" -) - -// TODO(pavelb): Gradually replace these everywhere. -type ( - Order = model.Order - OrderItem = model.OrderItem - CreateCheckoutSessionResponse = model.CreateCheckoutSessionResponse - Issuer = model.Issuer -) - -func decodeAndUnmarshalSku(sku string) (*macaroon.Macaroon, error) { - macBytes, err := macaroon.Base64Decode([]byte(sku)) - if err != nil { - return nil, fmt.Errorf("failed to b64 decode sku token: %w", err) - } - mac := &macaroon.Macaroon{} - if err = mac.UnmarshalBinary(macBytes); err != nil { - return nil, fmt.Errorf("failed to unmarshal sku token: %w", err) - } - - return mac, nil -} - -// CreateOrderItemFromMacaroon creates an order item from a macaroon -func (s *Service) CreateOrderItemFromMacaroon(ctx context.Context, sku string, quantity int) (*OrderItem, []string, *model.IssuerConfig, error) { - sublogger := logging.Logger(ctx, "CreateOrderItemFromMacaroon") - - // validation prior to decoding/unmarshalling - valid, err := validateHardcodedSku(ctx, sku) - if err != nil { - sublogger.Error().Err(err).Msg("failed to validate sku") - return nil, nil, nil, fmt.Errorf("failed to validate sku: %w", err) - } - - // perform validation - if !valid { - sublogger.Error().Err(err).Msg("invalid sku") - return nil, nil, nil, model.ErrInvalidSKU - } - - // read the macaroon, its valid - mac, err := decodeAndUnmarshalSku(sku) - if err != nil { - sublogger.Error().Err(err).Msg("failed to decode sku") - return nil, nil, nil, fmt.Errorf("failed to create order item from macaroon: %w", err) - } - - caveats := mac.Caveats() - var allowedPaymentMethods []string - orderItem := OrderItem{} - orderItem.Quantity = quantity - - orderItem.Location.String = mac.Location() - orderItem.Location.Valid = true - - issuerConfig := &model.IssuerConfig{ - Buffer: defaultBuffer, - Overlap: defaultOverlap, - } - - for i := 0; i < len(caveats); i++ { - caveat := mac.Caveats()[i] - values := strings.Split(string(caveat.Id), "=") - key := strings.TrimSpace(values[0]) - value := strings.TrimSpace(strings.Join(values[1:], "=")) - - switch key { - case "sku": - orderItem.SKU = value - case "price", "amount": - orderItem.Price, err = decimal.NewFromString(value) - if err != nil { - return nil, nil, nil, err - } - case "description": - orderItem.Description.String = value - orderItem.Description.Valid = true - case "currency": - orderItem.Currency = value - case "credential_type": - orderItem.CredentialType = value - case "issuance_interval": - orderItem.IssuanceIntervalISO = &value - case "credential_valid_duration": - // actually the expires time - orderItem.ValidFor = new(time.Duration) - id, err := timeutils.ParseDuration(value) - if err != nil { - sublogger.Error().Err(err).Msg("failed to decode sku credential_valid_duration") - return nil, nil, nil, fmt.Errorf("failed to unmarshal macaroon metadata: %w", err) - } - t, err := id.FromNow() - if err != nil { - sublogger.Error().Err(err).Msg("failed to decode sku credential_valid_duration") - return nil, nil, nil, fmt.Errorf("failed to unmarshal macaroon metadata: %w", err) - } - *orderItem.ValidFor = time.Until(*t) - orderItem.ValidForISO = &value - case "each_credential_valid_duration": - // for time aware issuers we need to explain per order item - // what the duration of each credential is - _, err := timeutils.ParseDuration(value) // parse the duration - if err != nil { - sublogger.Error().Err(err).Msg("failed to decode sku each_credential_valid_duration") - return nil, nil, nil, fmt.Errorf("failed to unmarshal macaroon metadata: %w", err) - } - // set the duration iso for the order item - orderItem.EachCredentialValidForISO = &value - case "issuer_token_buffer": - buffer, err := strconv.Atoi(value) - if err != nil { - return nil, nil, nil, fmt.Errorf("error converting buffer for order item %s: %w", orderItem.ID, err) - } - issuerConfig.Buffer = buffer - case "issuer_token_overlap": - overlap, err := strconv.Atoi(value) - if err != nil { - return nil, nil, nil, fmt.Errorf("error converting overlap for order item %s: %w", orderItem.ID, err) - } - issuerConfig.Overlap = overlap - case "allowed_payment_methods": - allowedPaymentMethods = strings.Split(value, ",") - case "metadata": - err := json.Unmarshal([]byte(value), &orderItem.Metadata) - sublogger.Debug().Str("value", value).Msg("metadata string") - sublogger.Debug().Str("metadata", fmt.Sprintf("%+v", orderItem.Metadata)).Msg("metadata structure") - if err != nil { - sublogger.Error().Err(err).Msg("failed to decode sku metadata") - return nil, nil, nil, fmt.Errorf("failed to unmarshal macaroon metadata: %w", err) - } - } - } - - newQuantity := decimal.NewFromInt(int64(orderItem.Quantity)) - orderItem.Subtotal = orderItem.Price.Mul(newQuantity) - - return &orderItem, allowedPaymentMethods, issuerConfig, nil -} - -func getEmailFromCheckoutSession(stripeSession *stripe.CheckoutSession) string { - // has an existing checkout session - var email string - if stripeSession == nil { - // stripe session does not exist - return email - } - if stripeSession.CustomerEmail != "" { - // if the email was stored on the stripe session customer email, use it - email = stripeSession.CustomerEmail - } else if stripeSession.Customer != nil && stripeSession.Customer.Email != "" { - // if the stripe session has a customer record, with an email, use it - email = stripeSession.Customer.Email - } - // if there is no record of an email, stripe will ask for it and make a new customer - return email -} - -// RenewOrder updates the order status to paid and records payment history. -// -// Status should either be one of pending, paid, fulfilled, or canceled. -func (s *Service) RenewOrder(ctx context.Context, orderID uuid.UUID) error { - if err := s.Datastore.UpdateOrder(orderID, OrderStatusPaid); err != nil { - return fmt.Errorf("failed to set order status to paid: %w", err) - } - - return s.DeleteOrderCreds(ctx, orderID, true) -} diff --git a/services/skus/order_test.go b/services/skus/order_test.go deleted file mode 100644 index dbc2b5d3a..000000000 --- a/services/skus/order_test.go +++ /dev/null @@ -1,201 +0,0 @@ -//go:build integration - -package skus - -import ( - "context" - "encoding/hex" - "strconv" - "strings" - "testing" - - "github.com/asaskevich/govalidator" - "github.com/stretchr/testify/suite" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/test" - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/storage/repository" - macarooncmd "github.com/brave-intl/bat-go/tools/macaroon/cmd" -) - -type OrderTestSuite struct { - service *Service - suite.Suite -} - -func TestOrderTestSuite(t *testing.T) { - suite.Run(t, new(OrderTestSuite)) -} - -func (suite *OrderTestSuite) SetupSuite() { - govalidator.SetFieldsRequiredByDefault(true) - pg, err := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.Require().NoError(err, "Failed to get postgres conn") - - m, err := pg.NewMigrate() - suite.Require().NoError(err, "Failed to create migrate instance") - - ver, dirty, _ := m.Version() - if dirty { - suite.Require().NoError(m.Force(int(ver))) - } - if ver > 0 { - suite.Require().NoError(m.Down(), "Failed to migrate down cleanly") - } - - EncryptionKey = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0" - InitEncryptionKeys() - - suite.Require().NoError(pg.Migrate(), "Failed to fully migrate") - suite.service = &Service{ - Datastore: pg, - } -} - -func (suite *OrderTestSuite) TearDownTest() { - suite.CleanDB() -} - -func (suite *OrderTestSuite) CleanDB() { - tables := []string{"api_keys"} - - pg, err := NewPostgres( - repository.NewOrder(), - repository.NewOrderItem(), - repository.NewOrderPayHistory(), - repository.NewIssuer(), - "", false, "", - ) - suite.Require().NoError(err, "Failed to get postgres conn") - - for _, table := range tables { - _, err = pg.RawDB().Exec("delete from " + table) - suite.Require().NoError(err, "Failed to get clean table") - } -} - -func (suite *OrderTestSuite) TestCreateOrderItemFromMacaroon() { - // encrypt merchant key - cipher, nonce, err := cryptography.EncryptMessage(byteEncryptionKey, []byte("testing123")) - suite.Require().NoError(err) - - // create key in db for our brave.com location - _, err = suite.service.Datastore.CreateKey("brave.com", "brave.com", hex.EncodeToString(cipher), hex.EncodeToString(nonce[:])) - suite.Require().NoError(err) - - c := macarooncmd.Caveats{ - "sku": "sku", - "price": "5.01", - "description": "coffee", - "currency": "usd", - "credential_type": "time_bound", - "allowed_payment_methods": "stripe", - "metadata": ` - { - "stripe_product_id":"stripe_product_id", - "stripe_success_url":"stripe_success_url", - "stripe_cancel_url":"stripe_cancel_url" - } - `, - } - - // create sku using key - t := macarooncmd.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macarooncmd.Caveats{c}, - } - - sku, err := t.Generate("testing123") - suite.Require().NoError(err) - - // hacky add to skuMap - skuMap["development"][sku] = true - - ctx := context.WithValue(context.Background(), appctx.EnvironmentCTXKey, "development") - - orderItem, apm, issuerConf, err := suite.service.CreateOrderItemFromMacaroon(ctx, sku, 1) - suite.Require().NoError(err) - - suite.assertSuccess(orderItem, apm, &model.IssuerConfig{ - Buffer: defaultBuffer, - Overlap: defaultOverlap, - }, issuerConf) - - badSku, err := t.Generate("321testing") - suite.Require().NoError(err) - - ctx = context.WithValue(context.Background(), appctx.EnvironmentCTXKey, "development") - _, _, _, err = suite.service.CreateOrderItemFromMacaroon(ctx, badSku, 1) - suite.Require().Equal(err.Error(), "Invalid SKU Token provided in request") -} - -func (suite *OrderTestSuite) TestCreateOrderItemFromMacaroon_WithBufferAndOverlap() { - // encrypt merchant key - cipher, nonce, err := cryptography.EncryptMessage(byteEncryptionKey, []byte("testing123")) - suite.Require().NoError(err) - - // create key in db for our brave.com location - _, err = suite.service.Datastore.CreateKey("brave.com", "brave.com", hex.EncodeToString(cipher), hex.EncodeToString(nonce[:])) - suite.Require().NoError(err) - - expectedIC := &model.IssuerConfig{ - Buffer: test.RandomInt(), - Overlap: test.RandomInt(), - } - - c := macarooncmd.Caveats{ - "sku": "sku", - "price": "5.01", - "description": "coffee", - "currency": "usd", - "credential_type": "time_bound", - "allowed_payment_methods": "stripe", - "issuer_token_buffer": strconv.Itoa(expectedIC.Buffer), - "issuer_token_overlap": strconv.Itoa(expectedIC.Overlap), - "metadata": ` - { - "stripe_product_id":"stripe_product_id", - "stripe_success_url":"stripe_success_url", - "stripe_cancel_url":"stripe_cancel_url" - } - `, - } - - // create sku using key - t := macarooncmd.Token{ - ID: "id", Version: 2, Location: "brave.com", - FirstPartyCaveats: []macarooncmd.Caveats{c}, - } - - sku, err := t.Generate("testing123") - suite.Require().NoError(err) - - // hacky add to skuMap - skuMap["development"][sku] = true - - ctx := context.WithValue(context.Background(), appctx.EnvironmentCTXKey, "development") - - orderItem, apm, issuerConf, err := suite.service.CreateOrderItemFromMacaroon(ctx, sku, 1) - suite.Require().NoError(err) - - suite.assertSuccess(orderItem, apm, expectedIC, issuerConf) -} - -func (suite *OrderTestSuite) assertSuccess(item *OrderItem, apm []string, expCfg, cfg *model.IssuerConfig) { - suite.Assert().Equal("stripe", strings.Join(apm, ",")) - suite.Assert().Equal("usd", item.Currency) - suite.Assert().Equal("sku", item.SKU) - suite.Assert().Equal("5.01", item.Price.String()) - suite.Assert().Equal("coffee", item.Description.String) - suite.Assert().Equal("brave.com", item.Location.String) - suite.Assert().Equal(expCfg.Buffer, cfg.Buffer) - suite.Assert().Equal(expCfg.Overlap, cfg.Overlap) -} diff --git a/services/skus/receipt.go b/services/skus/receipt.go deleted file mode 100644 index 127c02ab0..000000000 --- a/services/skus/receipt.go +++ /dev/null @@ -1,287 +0,0 @@ -package skus - -import ( - "context" - "crypto/ecdsa" - "crypto/x509" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/http/httputil" - "strings" - "time" - - "github.com/awa/go-iap/appstore" - "github.com/awa/go-iap/playstore" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" -) - -const ( - androidPaymentStatePending int64 = iota - androidPaymentStatePaid - androidPaymentStateTrial - androidPaymentStatePendingDeferred - - androidCancelReasonUser int64 = 0 - androidCancelReasonSystem int64 = 1 - androidCancelReasonReplaced int64 = 2 - androidCancelReasonDeveloper int64 = 3 -) - -var ( - receiptValidationFns = map[Vendor]func(context.Context, interface{}) (string, error){ - appleVendor: validateIOSReceipt, - googleVendor: validateAndroidReceipt, - } - iosClient *appstore.Client - androidClient *playstore.Client - errClientMisconfigured = errors.New("misconfigured client") - - errPurchaseUserCanceled = errors.New("purchase is canceled by user") - errPurchaseSystemCanceled = errors.New("purchase is canceled by google playstore") - errPurchaseReplacedCanceled = errors.New("purchase is canceled and replaced") - errPurchaseDeveloperCanceled = errors.New("purchase is canceled by developer") - - errPurchasePending = errors.New("purchase is pending") - errPurchaseDeferred = errors.New("purchase is deferred") - errPurchaseStatusUnknown = errors.New("purchase status is unknown") - errPurchaseFailed = errors.New("purchase failed") - - errPurchaseExpired = errors.New("purchase expired") - - purchasePendingErrCode = "purchase_pending" - purchaseDeferredErrCode = "purchase_deferred" - purchaseStatusUnknownErrCode = "purchase_status_unknown" - purchaseFailedErrCode = "purchase_failed" - purchaseValidationErrCode = "validation_failed" -) - -type dumpTransport struct{} - -func (dt *dumpTransport) RoundTrip(r *http.Request) (*http.Response, error) { - logger := logging.Logger(r.Context(), "skus").With().Str("func", "RoundTrip").Logger() - - dump, err := httputil.DumpRequestOut(r, true) - if err != nil { - logger.Error().Err(err).Msg("failed to dump request") - } - logger.Debug().Msgf("****REQUEST****\n%q\n", dump) - - resp, rtErr := http.DefaultTransport.RoundTrip(r) - - dump, err = httputil.DumpResponse(resp, true) - if err != nil { - logger.Error().Err(err).Msg("failed to dump response") - } - logger.Debug().Msgf("****RESPONSE****\n%q\n****************\n\n", dump) - - return resp, rtErr -} - -func initClients(ctx context.Context) { - - var logClient = &http.Client{ - Transport: &dumpTransport{}, - } - - logger := logging.Logger(ctx, "skus").With().Str("func", "initClients").Logger() - iosClient = appstore.New() - - if jsonKey, ok := ctx.Value(appctx.PlaystoreJSONKeyCTXKey).([]byte); ok { - var err error - androidClient, err = playstore.NewWithClient(jsonKey, logClient) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize android client") - } - } -} - -// validateIOSReceipt - validate apple receipt with their apis -func validateIOSReceipt(ctx context.Context, receipt interface{}) (string, error) { - logger := logging.Logger(ctx, "skus").With().Str("func", "validateIOSReceipt").Logger() - - // get the shared key from the context - sharedKey, sharedKeyOK := ctx.Value(appctx.AppleReceiptSharedKeyCTXKey).(string) - - if iosClient != nil { - // handle v1 receipt type - if v, ok := receipt.(SubmitReceiptRequestV1); ok { - req := appstore.IAPRequest{ - ReceiptData: v.Blob, - ExcludeOldTransactions: true, - } - if sharedKeyOK && len(sharedKey) > 0 { - req.Password = sharedKey - } - resp := &appstore.IAPResponse{} - if err := iosClient.Verify(ctx, req, resp); err != nil { - logger.Error().Err(err).Msg("failed to verify receipt") - return "", fmt.Errorf("failed to verify receipt: %w", err) - } - logger.Debug().Msg(fmt.Sprintf("%+v", resp)) - // get the transaction id back - if len(resp.Receipt.InApp) < 1 { - logger.Error().Msg("failed to verify receipt, no in app info") - return "", fmt.Errorf("failed to verify receipt, no in app info in response") - } - return resp.Receipt.InApp[0].OriginalTransactionID, nil - } - } - logger.Error().Msg("client is not configured") - return "", errClientMisconfigured -} - -// validateAndroidReceipt - validate android receipt with their apis -func validateAndroidReceipt(ctx context.Context, receipt interface{}) (string, error) { - logger := logging.Logger(ctx, "skus").With().Str("func", "validateAndroidReceipt").Logger() - if androidClient != nil { - if v, ok := receipt.(SubmitReceiptRequestV1); ok { - logger.Debug().Str("receipt", fmt.Sprintf("%+v", v)).Msg("about to verify subscription") - // handle v1 receipt type - resp, err := androidClient.VerifySubscription(ctx, v.Package, v.SubscriptionID, v.Blob) - if err != nil { - logger.Error().Err(err).Msg("failed to verify subscription") - return "", errPurchaseFailed - } - - // is order expired? - if time.Unix(0, resp.ExpiryTimeMillis*int64(time.Millisecond)).Before(time.Now()) { - return "", errPurchaseExpired - } - - logger.Debug().Msgf("resp: %+v", resp) - if resp.PaymentState != nil { - // check that the order was paid - switch *resp.PaymentState { - case androidPaymentStatePaid, androidPaymentStateTrial: - break - case androidPaymentStatePending: - // is there a cancel reason? - switch resp.CancelReason { - case androidCancelReasonUser: - return "", errPurchaseUserCanceled - case androidCancelReasonSystem: - return "", errPurchaseSystemCanceled - case androidCancelReasonReplaced: - return "", errPurchaseReplacedCanceled - case androidCancelReasonDeveloper: - return "", errPurchaseDeveloperCanceled - } - return "", errPurchasePending - case androidPaymentStatePendingDeferred: - return "", errPurchaseDeferred - default: - return "", errPurchaseStatusUnknown - } - return v.Blob, nil - } - logger.Error().Err(err).Msg("failed to verify subscription: no payment state") - return "", errPurchaseFailed - } - } - logger.Error().Msg("client is not configured") - return "", errClientMisconfigured -} - -// get the public key from the jws header -func extractPublicKey(jwsToken string) (*ecdsa.PublicKey, error) { - certStr, err := extractHeaderByIndex(jwsToken, 0) - if err != nil { - return nil, err - } - - cert, err := x509.ParseCertificate(certStr) - if err != nil { - return nil, err - } - - switch pk := cert.PublicKey.(type) { - case *ecdsa.PublicKey: - return pk, nil - default: - return nil, errors.New("appstore public key must be of type ecdsa.PublicKey") - } -} - -func extractHeaderByIndex(tokenStr string, index int) ([]byte, error) { - if index > 2 { - return nil, errors.New("invalid index") - } - - tokenArr := strings.Split(tokenStr, ".") - headerByte, err := base64.RawStdEncoding.DecodeString(tokenArr[0]) - if err != nil { - return nil, err - } - - type Header struct { - Alg string `json:"alg"` - X5c []string `json:"x5c"` - } - var header Header - err = json.Unmarshal(headerByte, &header) - if err != nil { - return nil, err - } - - certByte, err := base64.StdEncoding.DecodeString(header.X5c[index]) - if err != nil { - return nil, err - } - - return certByte, nil -} - -func verifyCert(certByte, intermediaCertStr []byte) error { - roots := x509.NewCertPool() - ok := roots.AppendCertsFromPEM([]byte(appleRootPEM)) - if !ok { - return errors.New("failed to parse root certificate") - } - - interCert, err := x509.ParseCertificate(intermediaCertStr) - if err != nil { - return errors.New("failed to parse intermedia certificate") - } - intermedia := x509.NewCertPool() - intermedia.AddCert(interCert) - - cert, err := x509.ParseCertificate(certByte) - if err != nil { - return err - } - - opts := x509.VerifyOptions{ - Roots: roots, - Intermediates: intermedia, - } - - _, err = cert.Verify(opts) - if err != nil { - return err - } - - return nil -} - -// rootPEM is from `openssl x509 -inform der -in AppleRootCA-G3.cer -out apple_root.pem` -const appleRootPEM = ` ------BEGIN CERTIFICATE----- -MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS -QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u -IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN -MTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS -b290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y -aXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49 -AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf -TjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517 -IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA -MGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4 -at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM -6BgD56KyKA== ------END CERTIFICATE----- -` diff --git a/services/skus/response.go b/services/skus/response.go deleted file mode 100644 index 7739ca540..000000000 --- a/services/skus/response.go +++ /dev/null @@ -1,7 +0,0 @@ -package skus - -// SubmitReceiptResponseV1 - response from submit receipt -type SubmitReceiptResponseV1 struct { - ExternalID string `json:"externalId"` - Vendor string `json:"vendor"` -} diff --git a/services/skus/service.go b/services/skus/service.go deleted file mode 100644 index 9625cc459..000000000 --- a/services/skus/service.go +++ /dev/null @@ -1,1946 +0,0 @@ -package skus - -import ( - "context" - "database/sql" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/url" - "os" - "strings" - "sync" - "time" - - "github.com/asaskevich/govalidator" - "github.com/awa/go-iap/appstore" - "github.com/getsentry/sentry-go" - "github.com/jmoiron/sqlx" - "github.com/lib/pq" - "github.com/linkedin/goavro" - uuid "github.com/satori/go.uuid" - "github.com/segmentio/kafka-go" - "github.com/shopspring/decimal" - "github.com/stripe/stripe-go/v72" - "github.com/stripe/stripe-go/v72/checkout/session" - "github.com/stripe/stripe-go/v72/client" - "github.com/stripe/stripe-go/v72/sub" - - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - srv "github.com/brave-intl/bat-go/libs/service" - timeutils "github.com/brave-intl/bat-go/libs/time" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/libs/clients/cbr" - "github.com/brave-intl/bat-go/libs/clients/gemini" - "github.com/brave-intl/bat-go/libs/clients/radom" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/wallet/provider" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/wallet" -) - -var ( - errSetRetryAfter = errors.New("set retry-after") - errClosingResource = errors.New("error closing resource") - errInvalidRadomURL = model.Error("service: invalid radom url") - errGeminiClientNotConfigured = errors.New("service: gemini client not configured") - errLegacyOutboxNotFound = model.Error("error no order credentials have been submitted for signing") - errWrongOrderIDForRequestID = model.Error("signed request order id does not belong to request id") - errLegacySUCredsNotFound = model.Error("credentials do not exist") - - voteTopic = os.Getenv("ENV") + ".payment.vote" - - // TODO address in kafka refactor. Check topics are correct - // kafka topic for requesting order credentials are signed, write to by sku service - kafkaUnsignedOrderCredsTopic = os.Getenv("GRANT_CBP_SIGN_PRODUCER_TOPIC") - - // kafka topic which receives order creds once they have been signed, read by sku service - kafkaSignedOrderCredsTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC") - kafkaSignedOrderCredsDLQTopic = os.Getenv("GRANT_CBP_SIGN_CONSUMER_TOPIC_DLQ") - kafkaSignedRequestReaderGroupID = os.Getenv("KAFKA_CONSUMER_GROUP_SIGNED_ORDER_CREDENTIALS") -) - -const ( - // TODO(pavelb): Gradually replace it everywhere. - // - // OrderStatusCanceled - string literal used in db for canceled status - OrderStatusCanceled = model.OrderStatusCanceled - // OrderStatusPaid - string literal used in db for canceled status - OrderStatusPaid = model.OrderStatusPaid - // OrderStatusPending - string literal used in db for pending status - OrderStatusPending = model.OrderStatusPending -) - -// Default issuer V3 config default values -const ( - defaultBuffer = 30 - defaultOverlap = 5 -) - -type orderStoreSvc interface { - Get(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) -} - -// Service contains datastore -type Service struct { - orderRepo orderStoreSvc - issuerRepo issuerStore - - // TODO: Eventually remove it. - Datastore Datastore - - wallet *wallet.Service - cbClient cbr.Client - geminiClient gemini.Client - geminiConf *gemini.Conf - scClient *client.API - codecs map[string]*goavro.Codec - kafkaWriter *kafka.Writer - kafkaDialer *kafka.Dialer - jobs []srv.Job - pauseVoteUntil time.Time - pauseVoteUntilMu sync.RWMutex - retry backoff.RetryFunc - radomClient *radom.InstrumentedClient - radomSellerAddress string -} - -// PauseWorker - pause worker until time specified -func (s *Service) PauseWorker(until time.Time) { - s.pauseVoteUntilMu.Lock() - defer s.pauseVoteUntilMu.Unlock() - s.pauseVoteUntil = until -} - -// IsPaused - is the worker paused? -func (s *Service) IsPaused() bool { - s.pauseVoteUntilMu.RLock() - defer s.pauseVoteUntilMu.RUnlock() - return time.Now().Before(s.pauseVoteUntil) -} - -// Jobs - Implement srv.JobService interface -func (s *Service) Jobs() []srv.Job { - return s.jobs -} - -// InitKafka by creating a kafka writer and creating local copies of codecs -func (s *Service) InitKafka(ctx context.Context) error { - // TODO address in kafka refactor - // passing an empty string will not set topic on writer, so it can be defined at message write time - var err error - s.kafkaWriter, s.kafkaDialer, err = kafkautils.InitKafkaWriter(ctx, "") - if err != nil { - return fmt.Errorf("failed to initialize kafka: %w", err) - } - - s.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "vote": voteSchema, - kafkaUnsignedOrderCredsTopic: signingOrderRequestSchema, - kafkaSignedOrderCredsTopic: signingOrderResultSchema, - }) - - if err != nil { - return fmt.Errorf("failed to generate codecs kafka: %w", err) - } - return nil -} - -// InitService creates a service using the passed datastore and clients configured from the environment. -func InitService(ctx context.Context, datastore Datastore, walletService *wallet.Service, orderRepo orderStoreSvc, issuerRepo issuerStore) (*Service, error) { - sublogger := logging.Logger(ctx, "payments").With().Str("func", "InitService").Logger() - // setup the in app purchase clients - initClients(ctx) - - // setup stripe if exists in context and enabled - scClient := &client.API{} - if enabled, ok := ctx.Value(appctx.StripeEnabledCTXKey).(bool); ok && enabled { - sublogger.Debug().Msg("stripe enabled") - var err error - stripe.Key, err = appctx.GetStringFromContext(ctx, appctx.StripeSecretCTXKey) - if err != nil { - sublogger.Panic().Err(err).Msg("failed to get Stripe secret from context, and Stripe enabled") - } - // initialize stripe client - scClient.Init(stripe.Key, nil) - } - - var ( - radomSellerAddress string - radomClient *radom.InstrumentedClient - ) - - // setup radom if exists in context and enabled - if enabled, ok := ctx.Value(appctx.RadomEnabledCTXKey).(bool); ok && enabled { - sublogger.Debug().Msg("radom enabled") - var err error - radomSellerAddress, err = appctx.GetStringFromContext(ctx, appctx.RadomSellerAddressCTXKey) - if err != nil { - sublogger.Error().Err(err).Msg("failed to get Stripe secret from context, and Stripe enabled") - return nil, err - } - - srvURL := os.Getenv("RADOM_SERVER") - if srvURL == "" { - return nil, errInvalidRadomURL - } - - rdSecret := os.Getenv("RADOM_SECRET") - proxyAddr := os.Getenv("HTTP_PROXY") - - radomClient, err = radom.NewInstrumented(srvURL, rdSecret, proxyAddr) - if err != nil { - return nil, err - } - } - - cbClient, err := cbr.New() - if err != nil { - return nil, err - } - - var ( - geminiClient gemini.Client - geminiConf *gemini.Conf - ) - if os.Getenv("GEMINI_ENABLED") == "true" { - apiKey, clientID, settlementAddress, apiSecret, err := getGeminiInfoFromCtx(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get gemini info: %w", err) - } - - // get the correct env variables for bulk pay API call - geminiConf = &gemini.Conf{ - ClientID: clientID, - APIKey: apiKey, - Secret: apiSecret, - SettlementAddress: settlementAddress, - } - - geminiClient, err = gemini.New() - if err != nil { - return nil, fmt.Errorf("failed to create gemini client: %w", err) - } - } - - service := &Service{ - orderRepo: orderRepo, - issuerRepo: issuerRepo, - Datastore: datastore, - - wallet: walletService, - geminiClient: geminiClient, - geminiConf: geminiConf, - cbClient: cbClient, - scClient: scClient, - pauseVoteUntilMu: sync.RWMutex{}, - retry: backoff.Retry, - radomClient: radomClient, - radomSellerAddress: radomSellerAddress, - } - - // setup runnable jobs - service.jobs = []srv.Job{ - { - Func: service.RunNextVoteDrainJob, - Cadence: 2 * time.Second, - Workers: 1, - }, - { - Func: service.RunSendSigningRequestJob, - Cadence: 100 * time.Millisecond, - Workers: 1, - }, - } - - if err := service.InitKafka(ctx); err != nil { - return nil, err - } - - ctx = context.WithValue(ctx, appctx.KafkaBrokersCTXKey, os.Getenv("KAFKA_BROKERS")) - - if enabled, ok := ctx.Value(appctx.SkusEnableStoreSignedOrderCredsConsumer).(bool); ok && enabled { - if consumers, ok := ctx.Value(appctx.SkusNumberStoreSignedOrderCredsConsumer).(int); ok { - for i := 0; i < consumers; i++ { - go service.RunStoreSignedOrderCredentials(ctx, 10*time.Second) - } - } - } - - return service, nil -} - -// ExternalIDExists checks if this external id has been used on any orders -func (s *Service) ExternalIDExists(ctx context.Context, externalID string) (bool, error) { - return s.Datastore.ExternalIDExists(ctx, externalID) -} - -// CreateOrderFromRequest creates an order from the request -func (s *Service) CreateOrderFromRequest(ctx context.Context, req model.CreateOrderRequest) (*Order, error) { - const merchantID = "brave.com" - - var ( - totalPrice = decimal.New(0, 0) - currency string - orderItems []OrderItem - location string - validFor *time.Duration - stripeSuccessURI string - stripeCancelURI string - allowedPaymentMethods []string - numIntervals int - numPerInterval = 2 // two per interval credentials to be submitted for signing - ) - - for i := 0; i < len(req.Items); i++ { - orderItem, pm, issuerConfig, err := s.CreateOrderItemFromMacaroon(ctx, req.Items[i].SKU, req.Items[i].Quantity) - if err != nil { - return nil, err - } - - // TODO: we ultimately need to figure out how to provision numPerInterval and numIntervals - // on the order item instead of the order itself to support multiple orders with - // different time limited v2 issuers. - // For now leo sku needs 192 as num per interval. - if orderItem.IsLeo() { - numPerInterval = 192 // 192 credentials per day for leo - } - - // Create issuer for sku. This only happens when a new sku is created. - switch orderItem.CredentialType { - case singleUse: - if err := s.CreateIssuer(ctx, s.Datastore.RawDB(), merchantID, orderItem); err != nil { - return nil, errorutils.Wrap(err, "error finding issuer") - } - case timeLimitedV2: - if err := s.CreateIssuerV3(ctx, s.Datastore.RawDB(), merchantID, orderItem, *issuerConfig); err != nil { - return nil, fmt.Errorf( - "error creating issuer for merchantID %s and sku %s: %w", - merchantID, orderItem.SKU, err, - ) - } - - // set num tokens and token multi - numIntervals = issuerConfig.Buffer + issuerConfig.Overlap - } - - // make sure all the order item skus have the same allowed Payment Methods - if i >= 1 { - if err := model.EnsureEqualPaymentMethods(allowedPaymentMethods, pm); err != nil { - return nil, err - } - } else { - // first order item - allowedPaymentMethods = pm - } - - totalPrice = totalPrice.Add(orderItem.Subtotal) - - if location == "" { - location = orderItem.Location.String - } - - if orderItem.ValidFor != nil { - validFor = new(time.Duration) - *validFor = *orderItem.ValidFor - } - - if location != orderItem.Location.String { - return nil, errors.New("all order items must be from the same location") - } - if currency == "" { - currency = orderItem.Currency - } - if currency != orderItem.Currency { - return nil, errors.New("all order items must be the same currency") - } - - // stripe related - metadataStripeSuccessURI, ok := orderItem.Metadata["stripe_success_uri"].(string) - if ok { - if stripeSuccessURI == "" { - stripeSuccessURI = metadataStripeSuccessURI - } else if stripeSuccessURI != metadataStripeSuccessURI { - return nil, errors.New("all order items must have same stripe success uri") - } - } - - metadataStripeCancelURI, ok := orderItem.Metadata["stripe_cancel_uri"].(string) - if ok { - if stripeCancelURI == "" { - stripeCancelURI = metadataStripeCancelURI - } else if stripeCancelURI != metadataStripeCancelURI { - return nil, errors.New("all order items must have same stripe cancel uri") - } - } - - orderItems = append(orderItems, *orderItem) - } - - oreq := &model.OrderNew{ - MerchantID: merchantID, - Currency: currency, - Status: OrderStatusPending, - TotalPrice: totalPrice, - AllowedPaymentMethods: pq.StringArray(allowedPaymentMethods), - ValidFor: validFor, - } - - // Consider the order paid if it consists entirely of zero cost items (e.g. trials). - if oreq.TotalPrice.IsZero() { - oreq.Status = OrderStatusPaid - } - - if location != "" { - oreq.Location.Valid = true - oreq.Location.String = location - } - - tx, err := s.Datastore.BeginTx() - if err != nil { - return nil, err - } - defer func() { _ = tx.Rollback() }() - - order, err := s.Datastore.CreateOrder(ctx, tx, oreq, orderItems) - if err != nil { - return nil, fmt.Errorf("failed to create order: %w", err) - } - - if err := tx.Commit(); err != nil { - return nil, err - } - - if !order.IsPaid() { - switch { - case order.IsStripePayable(): - // brand-new order, contains an email in the request - session, err := order.CreateStripeCheckoutSession( - req.Email, - parseURLAddOrderIDParam(stripeSuccessURI, order.ID), - parseURLAddOrderIDParam(stripeCancelURI, order.ID), - order.GetTrialDays(), - ) - if err != nil { - return nil, fmt.Errorf("failed to create checkout session: %w", err) - } - - err = s.Datastore.AppendOrderMetadata(ctx, &order.ID, "stripeCheckoutSessionId", session.SessionID) - if err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - - case order.IsRadomPayable(): - session, err := order.CreateRadomCheckoutSession( - ctx, - s.radomClient, - s.radomSellerAddress, //TODO: fill in - ) - if err != nil { - return nil, fmt.Errorf("failed to create checkout session: %w", err) - } - - err = s.Datastore.AppendOrderMetadata(ctx, &order.ID, "radomCheckoutSessionId", session.SessionID) - if err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - } - - if numIntervals > 0 { - err = s.Datastore.AppendOrderMetadataInt(ctx, &order.ID, "numIntervals", numIntervals) - if err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - - if numPerInterval > 0 { - err = s.Datastore.AppendOrderMetadataInt(ctx, &order.ID, "numPerInterval", numPerInterval) - if err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - - return order, err -} - -// GetOrder - business logic for getting an order, needs to validate the checkout session is not expired -func (s *Service) GetOrder(orderID uuid.UUID) (*Order, error) { - // get the order - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return nil, fmt.Errorf("failed to get order (%s): %w", orderID.String(), err) - } - - if order != nil { - if !order.IsPaid() && order.IsStripePayable() { - order, err = s.TransformStripeOrder(order) - if err != nil { - return nil, fmt.Errorf("failed to transform stripe order (%s): %w", orderID.String(), err) - } - } - } - - return order, nil - -} - -// TransformStripeOrder updates checkout session if expired, checks the status of the checkout session. -func (s *Service) TransformStripeOrder(order *Order) (*Order, error) { - ctx := context.Background() - - // check if this order has an expired checkout session - expired, cs, err := s.Datastore.CheckExpiredCheckoutSession(order.ID) - if err != nil { - return nil, fmt.Errorf("failed to check for expired stripe checkout session: %w", err) - } - - if expired { - // get old checkout session from stripe by id - stripeSession, err := session.Get(cs, nil) - if err != nil { - return nil, fmt.Errorf("failed to get stripe checkout session: %w", err) - } - - checkoutSession, err := order.CreateStripeCheckoutSession( - getEmailFromCheckoutSession(stripeSession), - stripeSession.SuccessURL, stripeSession.CancelURL, - order.GetTrialDays(), - ) - if err != nil { - return nil, fmt.Errorf("failed to create checkout session: %w", err) - } - - err = s.Datastore.AppendOrderMetadata(ctx, &order.ID, "stripeCheckoutSessionId", checkoutSession.SessionID) - if err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - - // if this is a stripe order, and there is a checkout session, we actually need to check it with - // stripe, as the redirect flow sometimes is too fast for the webhook to be delivered. - // exclude any order with a subscription identifier from stripe - if _, sOK := order.Metadata["stripeSubscriptionId"]; !sOK { - if cs, ok := order.Metadata["stripeCheckoutSessionId"].(string); ok && cs != "" { - // get old checkout session from stripe by id - sess, err := session.Get(cs, nil) - if err != nil { - return nil, fmt.Errorf("failed to get stripe checkout session: %w", err) - } - - // Set status to paid and the subscription id and if the session is actually paid. - if sess.PaymentStatus == "paid" { - if err = s.Datastore.UpdateOrder(order.ID, "paid"); err != nil { - return nil, fmt.Errorf("failed to update order to paid status: %w", err) - } - - if err := s.Datastore.AppendOrderMetadata(ctx, &order.ID, "stripeSubscriptionId", sess.Subscription.ID); err != nil { - return nil, fmt.Errorf("failed to update order to add the subscription id") - } - - if err := s.Datastore.AppendOrderMetadata(ctx, &order.ID, paymentProcessor, StripePaymentMethod); err != nil { - return nil, fmt.Errorf("failed to update order to add the payment processor") - } - } - } - } - - result, err := s.Datastore.GetOrder(order.ID) - if err != nil { - return nil, fmt.Errorf("failed to get order: %w", err) - } - - return result, nil -} - -// CancelOrder cancels an order, propagates to stripe if needed. -// -// TODO(pavelb): Refactor and make it precise. -// Currently, this method does something weird for the case when the order was not found in the DB. -// If we have an order id, but ended up without the order, that means either the id is wrong, -// or we somehow lost data. The latter is less likely. -// Yet we allow non-existing order ids to be searched for in Stripe, which is strange. -func (s *Service) CancelOrder(orderID uuid.UUID) error { - // Check the order, do we have a stripe subscription? - ok, subID, err := s.Datastore.IsStripeSub(orderID) - if err != nil && !errors.Is(err, model.ErrOrderNotFound) { - return fmt.Errorf("failed to check stripe subscription: %w", err) - } - - if ok && subID != "" { - // Cancel the stripe subscription. - if _, err := sub.Cancel(subID, nil); err != nil { - return fmt.Errorf("failed to cancel stripe subscription: %w", err) - } - - return s.Datastore.UpdateOrder(orderID, OrderStatusCanceled) - } - - // Try to find order in Stripe. - params := &stripe.SubscriptionSearchParams{} - params.Query = *stripe.String(fmt.Sprintf("status:'active' AND metadata['orderID']:'%s'", orderID.String())) - - ctx := context.TODO() - - iter := sub.Search(params) - for iter.Next() { - // we have a result, fix the stripe sub on the db record, and then cancel sub - subscription := iter.Subscription() - // cancel the stripe subscription - if _, err := sub.Cancel(subscription.ID, nil); err != nil { - return fmt.Errorf("failed to cancel stripe subscription: %w", err) - } - - if err := s.Datastore.AppendOrderMetadata(ctx, &orderID, "stripeSubscriptionId", subscription.ID); err != nil { - return fmt.Errorf("failed to update order metadata with subscription id: %w", err) - } - } - - return s.Datastore.UpdateOrder(orderID, OrderStatusCanceled) -} - -// SetOrderTrialDays set the order's free trial days -func (s *Service) SetOrderTrialDays(ctx context.Context, orderID *uuid.UUID, days int64) error { - // get the order - order, err := s.Datastore.SetOrderTrialDays(ctx, orderID, days) - if err != nil { - return fmt.Errorf("failed to set the order's trial days: %w", err) - } - - // recreate the stripe checkout session now that we have set the trial days on this order - if !order.IsPaid() && order.IsStripePayable() { - // get old checkout session from stripe by id - csID, ok := order.Metadata["stripeCheckoutSessionId"].(string) - if !ok { - return fmt.Errorf("failed to get checkout session id from metadata: %w", err) - } - stripeSession, err := session.Get(csID, nil) - if err != nil { - return fmt.Errorf("failed to get stripe checkout session: %w", err) - } - - checkoutSession, err := order.CreateStripeCheckoutSession( - getEmailFromCheckoutSession(stripeSession), - stripeSession.SuccessURL, stripeSession.CancelURL, - order.GetTrialDays(), - ) - if err != nil { - return fmt.Errorf("failed to create checkout session: %w", err) - } - - // overwrite the old checkout session - err = s.Datastore.AppendOrderMetadata(ctx, &order.ID, "stripeCheckoutSessionId", checkoutSession.SessionID) - if err != nil { - return fmt.Errorf("failed to update order metadata: %w", err) - } - } - - return nil -} - -// UpdateOrderStatus checks to see if an order has been paid and updates it if so -func (s *Service) UpdateOrderStatus(orderID uuid.UUID) error { - // get the order - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return err - } - - sum, err := s.Datastore.GetSumForTransactions(orderID) - if err != nil { - return err - } - - if sum.GreaterThanOrEqual(order.TotalPrice) { - err = s.Datastore.UpdateOrder(orderID, "paid") - if err != nil { - return err - } - } - - return nil -} - -// getCustodialTxFn - type definition of a get custodial tx function -// return amount, status, currency, kind, err -type getCustodialTxFn func(context.Context, string) (*decimal.Decimal, string, string, string, error) - -// get the uphold tx based on txRef -func getUpholdCustodialTx(ctx context.Context, txRef string) (*decimal.Decimal, string, string, string, error) { - var wallet uphold.Wallet - upholdTransaction, err := wallet.GetTransaction(ctx, txRef) - - if err != nil { - return nil, "", "", "", err - } - - amount := upholdTransaction.AltCurrency.FromProbi(upholdTransaction.Probi) - status := upholdTransaction.Status - currency := upholdTransaction.AltCurrency.String() - custodian := "uphold" - - // check if destination is the right address - if upholdTransaction.Destination != uphold.UpholdSettlementAddress { - return nil, "", "", custodian, errors.New("error recording transaction: invalid settlement address") - } - - return &amount, status, currency, custodian, nil -} - -// getUpholdCustodialTxWithRetries - the the custodial tx information from uphold with retries -func getUpholdCustodialTxWithRetries(ctx context.Context, txRef string) (*decimal.Decimal, string, string, string, error) { - - var ( - amount *decimal.Decimal - status string - currency string - custodian string - err error - ) - - // best effort to check that the tx is done processing -OUTER: - for i := 0; i < 5; i++ { - select { - case <-ctx.Done(): - break OUTER - case <-time.After(500 * time.Millisecond): - amount, status, currency, custodian, err = getUpholdCustodialTx(ctx, txRef) - if err != nil { - return nil, "", "", "", fmt.Errorf("failed to get uphold tx by txRef %s: %w", txRef, err) - } - if status != "processing" && status != "pending" { - break OUTER - } - } - } - - return amount, status, currency, custodian, nil -} - -// returns gemini client, api key, client id, settlement address, apiSecret, error -func getGeminiInfoFromCtx(ctx context.Context) (string, string, string, string, error) { - // get gemini client from context - apiKey, ok := ctx.Value(appctx.GeminiAPIKeyCTXKey).(string) - if !ok { - return "", "", "", "", fmt.Errorf("no gemini api key in ctx: %w", appctx.ErrNotInContext) - } - - // get gemini client id from context - clientID, ok := ctx.Value(appctx.GeminiBrowserClientIDCTXKey).(string) - if !ok { - return "", "", "", "", fmt.Errorf("no gemini browser client id in ctx: %w", appctx.ErrNotInContext) - } - - // get gemini settlement address from context - settlementAddress, ok := ctx.Value(appctx.GeminiSettlementAddressCTXKey).(string) - if !ok { - return "", "", "", "", fmt.Errorf("no gemini settlement address in ctx: %w", appctx.ErrNotInContext) - } - - // get gemini api secret from context - apiSecret, ok := ctx.Value(appctx.GeminiAPISecretCTXKey).(string) - if !ok { - return "", "", "", "", fmt.Errorf("no gemini api secret in ctx: %w", appctx.ErrNotInContext) - } - - return apiKey, clientID, settlementAddress, apiSecret, nil -} - -// getGeminiCustodialTx returns the custodial tx information from Gemini -func (s *Service) getGeminiCustodialTx(ctx context.Context, txRef string) (*decimal.Decimal, string, string, string, error) { - if s.geminiConf == nil { - return nil, "", "", "", errGeminiClientNotConfigured - } - - sublogger := logging.Logger(ctx, "payments").With(). - Str("func", "getGeminiCustodialTx"). - Logger() - - custodian := "gemini" - - // call client.CheckTxStatus - ctx = context.WithValue(ctx, appctx.GeminiAPISecretCTXKey, s.geminiConf.Secret) - resp, err := s.geminiClient.CheckTxStatus(ctx, s.geminiConf.APIKey, s.geminiConf.ClientID, txRef) - if err != nil { - sublogger.Error().Err(err).Msg("failed to check tx status") - return nil, "", "", custodian, fmt.Errorf("error getting tx status: %w", err) - } - - // check if destination is the right address - if *resp.Destination != s.geminiConf.SettlementAddress { - sublogger.Error().Err(err).Msg("settlement address does not match tx destination") - return nil, "", "", custodian, errors.New("error recording transaction: invalid settlement address") - } - - var ( - amount decimal.Decimal - status string - currency string - ) - // return back the amount - if resp.Amount != nil { - amount = *resp.Amount - } - if resp.Status != nil { - // response values are Titled from Gemini - status = strings.ToLower(*resp.Status) - } - if resp.Currency != nil { - currency = *resp.Currency - } - - return &amount, status, currency, custodian, nil -} - -// CreateTransactionFromRequest queries the endpoints and creates a transaction -func (s *Service) CreateTransactionFromRequest(ctx context.Context, req CreateTransactionRequest, orderID uuid.UUID, getCustodialTx getCustodialTxFn) (*Transaction, error) { - - sublogger := logging.Logger(ctx, "payments").With(). - Str("func", "CreateAnonCardTransaction"). - Logger() - - // get the information from the custodian - amount, status, currency, kind, err := getCustodialTx(ctx, req.ExternalTransactionID) - if err != nil { - sublogger.Error().Err(err).Msg("failed to get and validate custodian transaction") - return nil, errorutils.Wrap(err, fmt.Sprintf("failed to get get and validate custodialtx: %s", err.Error())) - } - - transaction, err := s.Datastore.CreateTransaction(orderID, req.ExternalTransactionID, status, currency, kind, *amount) - if err != nil { - sublogger.Error().Err(err).Msg("failed to create the transaction for the order") - return nil, errorutils.Wrap(err, "error recording transaction") - } - - isPaid, err := s.IsOrderPaid(transaction.OrderID) - if err != nil { - sublogger.Error().Err(err).Msg("failed to validate the order is paid based on transactions") - return nil, errorutils.Wrap(err, "error validating order is paid") - } - - // If the transaction that was satisifies the order then let's update the status - if isPaid { - err = s.Datastore.UpdateOrder(transaction.OrderID, "paid") - if err != nil { - sublogger.Error().Err(err).Msg("failed to set the status to paid") - return nil, errorutils.Wrap(err, "error updating order status") - } - } - - return transaction, err -} - -// UpdateTransactionFromRequest queries the endpoints and creates a transaciton -func (s *Service) UpdateTransactionFromRequest(ctx context.Context, req CreateTransactionRequest, orderID uuid.UUID, getCustodialTx getCustodialTxFn) (*Transaction, error) { - - sublogger := logging.Logger(ctx, "payments").With(). - Str("func", "UpdateTransactionFromRequest"). - Logger() - - // get the information from the custodian - amount, status, currency, kind, err := getCustodialTx(ctx, req.ExternalTransactionID) - if err != nil { - sublogger.Error().Err(err).Msg("failed to get and validate custodian transaction") - return nil, errorutils.Wrap(err, fmt.Sprintf("failed to get get and validate custodialtx: %s", err.Error())) - } - - transaction, err := s.Datastore.UpdateTransaction(orderID, req.ExternalTransactionID, status, currency, kind, *amount) - if err != nil { - sublogger.Error().Err(err).Msg("failed to create the transaction for the order") - return nil, errorutils.Wrap(err, "error recording transaction") - } - - isPaid, err := s.IsOrderPaid(transaction.OrderID) - if err != nil { - sublogger.Error().Err(err).Msg("failed to validate the order is paid based on transactions") - return nil, errorutils.Wrap(err, "error validating order is paid") - } - - // If the transaction that was satisifies the order then let's update the status - if isPaid { - err = s.Datastore.UpdateOrder(transaction.OrderID, "paid") - if err != nil { - sublogger.Error().Err(err).Msg("failed to set the status to paid") - return nil, errorutils.Wrap(err, "error updating order status") - } - } - - return transaction, err -} - -// CreateAnonCardTransaction takes a signed transaction and executes it on behalf of an anon card -func (s *Service) CreateAnonCardTransaction(ctx context.Context, walletID uuid.UUID, transaction string, orderID uuid.UUID) (*Transaction, error) { - - sublogger := logging.Logger(ctx, "payments").With(). - Str("func", "CreateAnonCardTransaction"). - Logger() - - txInfo, err := s.wallet.SubmitAnonCardTransaction( - ctx, - walletID, - transaction, - uphold.AnonCardSettlementAddress, - ) - if err != nil { - return nil, errorutils.Wrap(err, "error submitting anon card transaction") - } - - txInfo, err = s.waitForUpholdTxStatus(ctx, walletID, txInfo.ID, "completed") - if err != nil { - return nil, errorutils.Wrap(err, "error waiting for completed status for transaction") - } - - txn, err := s.Datastore.CreateTransaction(orderID, txInfo.ID, txInfo.Status, txInfo.DestCurrency, "anonymous-card", txInfo.DestAmount) - if err != nil { - return nil, errorutils.Wrap(err, "error recording anon card transaction") - } - - err = s.UpdateOrderStatus(orderID) - if err != nil { - sublogger.Error().Err(err).Msg("failed to update order status") - return nil, errorutils.Wrap(err, "error updating order status") - } - - return txn, err -} - -func (s *Service) waitForUpholdTxStatus(ctx context.Context, walletID uuid.UUID, txnID, status string) (*walletutils.TransactionInfo, error) { - info, err := s.wallet.GetWallet(ctx, walletID) - if err != nil { - return nil, err - } - - providerWallet, err := provider.GetWallet(ctx, *info) - if err != nil { - return nil, err - } - - upholdWallet, ok := providerWallet.(*uphold.Wallet) - if !ok { - return nil, errors.New("only uphold wallets are supported") - } - - var txInfo = &walletutils.TransactionInfo{ - ID: txnID, - } - // check status until it matches - for { - select { - case <-ctx.Done(): - return nil, errors.New("timeout waiting for correct status") - default: - txInfo, err = upholdWallet.GetTransaction(ctx, txInfo.ID) - if err != nil { - return nil, errorutils.Wrap(err, "error getting transaction") - } - if strings.ToLower(txInfo.Status) == status { - return txInfo, nil - } - <-time.After(1 * time.Second) - } - } -} - -// IsOrderPaid determines if the order has been paid -func (s *Service) IsOrderPaid(orderID uuid.UUID) (bool, error) { - // Now that the transaction has been created let's check to see if that fulfilled the order. - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return false, err - } - - sum, err := s.Datastore.GetSumForTransactions(orderID) - if err != nil { - return false, err - } - - return sum.GreaterThanOrEqual(order.TotalPrice), nil -} - -func parseURLAddOrderIDParam(u string, orderID uuid.UUID) string { - // add order id to the stripe success and cancel urls - surl, err := url.Parse(u) - if err == nil { - surlv := surl.Query() - surlv.Add("order_id", orderID.String()) - surl.RawQuery = surlv.Encode() - return surl.String() - } - // there was a parse error, return whatever was given - return u -} - -const ( - singleUse = "single-use" - timeLimited = "time-limited" - timeLimitedV2 = "time-limited-v2" -) - -var errInvalidCredentialType = errors.New("invalid credential type on order") - -// GetItemCredentials returns credentials based on the order, item and request id. -func (s *Service) GetItemCredentials(ctx context.Context, orderID, itemID, reqID uuid.UUID) (interface{}, int, error) { - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return nil, http.StatusNotFound, fmt.Errorf("failed to get order: %w", err) - } - - if order == nil { - return nil, http.StatusNotFound, fmt.Errorf("failed to get order: %w", err) - } - - item, ok := order.HasItem(itemID) - if !ok { - return nil, http.StatusNotFound, fmt.Errorf("failed to get item: %w", err) - } - - switch item.CredentialType { - case singleUse: - return s.GetSingleUseCreds(ctx, order.ID, itemID, reqID) - case timeLimited: - return s.GetTimeLimitedCreds(ctx, order, itemID, reqID) - case timeLimitedV2: - return s.GetTimeLimitedV2Creds(ctx, order.ID, itemID, reqID) - default: - return nil, http.StatusConflict, errInvalidCredentialType - } -} - -// GetCredentials returns credentials on the order. -// -// This is a legacy method. -// For backward compatibility, similar to creating credentials, it uses item id as request id. -func (s *Service) GetCredentials(ctx context.Context, orderID uuid.UUID) (interface{}, int, error) { - order, err := s.Datastore.GetOrder(orderID) - if err != nil { - return nil, http.StatusNotFound, fmt.Errorf("failed to get order: %w", err) - } - - if order == nil { - return nil, http.StatusNotFound, fmt.Errorf("failed to get order: %w", err) - } - - if len(order.Items) != 1 { - return nil, http.StatusConflict, model.Error("order must only have one item") - } - - itemID := order.Items[0].ID - - switch order.Items[0].CredentialType { - case singleUse: - return s.GetSingleUseCreds(ctx, order.ID, itemID, itemID) - case timeLimited: - return s.GetTimeLimitedCreds(ctx, order, itemID, itemID) - case timeLimitedV2: - return s.GetTimeLimitedV2Creds(ctx, order.ID, itemID, itemID) - default: - return nil, http.StatusConflict, errInvalidCredentialType - } -} - -// GetSingleUseCreds returns single use credentials for a given order, item and request. -// -// If the credentials have been submitted but not yet signed it returns a http.StatusAccepted and an empty body. -// If the credentials have been signed it will return a http.StatusOK and the order credentials. -func (s *Service) GetSingleUseCreds(ctx context.Context, orderID, itemID, reqID uuid.UUID) ([]OrderCreds, int, error) { - // Single use credentials retain the old semantics, only one request is ever allowed. - creds, err := s.Datastore.GetOrderCredsByItemID(orderID, itemID, false) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to get single use creds: %w", err) - } - - if creds != nil { - // TODO: Issues #1541 remove once all creds using RunOrderJob have need processed - if creds.SignedCreds == nil { - return nil, http.StatusAccepted, nil - } - - // TODO: End - return []OrderCreds{*creds}, http.StatusOK, nil - } - - outboxMessages, err := s.Datastore.GetSigningOrderRequestOutboxByRequestID(ctx, s.Datastore.RawDB(), reqID) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, http.StatusNotFound, errLegacySUCredsNotFound - } - - return nil, http.StatusInternalServerError, fmt.Errorf("error getting outbox messages: %w", err) - } - - if outboxMessages.CompletedAt == nil { - return nil, http.StatusAccepted, nil - } - - return nil, http.StatusInternalServerError, model.Error("unreachable condition") -} - -// GetTimeLimitedV2Creds returns all the tlv2 credentials for a given order, item and request id. -// -// If the credentials have been submitted but not yet signed it returns a http.StatusAccepted and an empty body. -// If the credentials have been signed it will return a http.StatusOK and the time limited v2 credentials. -// -// Browser's api_request_helper does not understand Go's nil slices, hence explicit empty slice is returned. -func (s *Service) GetTimeLimitedV2Creds(ctx context.Context, orderID, itemID, reqID uuid.UUID) ([]TimeAwareSubIssuedCreds, int, error) { - obmsg, err := s.Datastore.GetSigningOrderRequestOutboxByRequestID(ctx, s.Datastore.RawDB(), reqID) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return []TimeAwareSubIssuedCreds{}, http.StatusNotFound, errLegacyOutboxNotFound - } - - return []TimeAwareSubIssuedCreds{}, http.StatusInternalServerError, fmt.Errorf("error getting outbox messages: %w", err) - } - - if !uuid.Equal(obmsg.OrderID, orderID) { - return []TimeAwareSubIssuedCreds{}, http.StatusBadRequest, errWrongOrderIDForRequestID - } - - if obmsg.CompletedAt == nil { - // Get average of last 10 outbox messages duration as the retry after. - return []TimeAwareSubIssuedCreds{}, http.StatusAccepted, errSetRetryAfter - } - - creds, err := s.Datastore.GetTLV2Creds(ctx, s.Datastore.RawDB(), orderID, itemID, reqID) - if err != nil { - if errors.Is(err, errNoTLV2Creds) { - // Credentials could be signed, but nothing to return as they are all expired. - return []TimeAwareSubIssuedCreds{}, http.StatusOK, nil - } - - return []TimeAwareSubIssuedCreds{}, http.StatusInternalServerError, fmt.Errorf("error getting credentials: %w", err) - } - - return creds.Credentials, http.StatusOK, nil -} - -// GetActiveCredentialSigningKey get the current active signing key for this merchant -func (s *Service) GetActiveCredentialSigningKey(ctx context.Context, merchantID string) ([]byte, error) { - // sorted by name, created_at, first result is most recent - keys, err := s.Datastore.GetKeysByMerchant(merchantID, false) - if err != nil { - return nil, fmt.Errorf("error getting keys by merchant: %w", err) - } - if keys == nil || len(*keys) < 1 { - return nil, fmt.Errorf("merchant keys is nil") - } - - secret, err := (*keys)[0].GetSecretKey() - if err != nil { - return nil, fmt.Errorf("error getting key's secret value: %w", err) - } - if secret == nil { - return nil, fmt.Errorf("invalid empty value for secret key") - } - - return []byte(*secret), nil -} - -// GetCredentialSigningKeys get the current list of credential signing keys for this merchant -func (s *Service) GetCredentialSigningKeys(ctx context.Context, merchantID string) ([][]byte, error) { - var resp = [][]byte{} - keys, err := s.Datastore.GetKeysByMerchant(merchantID, false) - if err != nil { - return nil, fmt.Errorf("error getting keys by merchant: %w", err) - } - if keys == nil { - return nil, fmt.Errorf("merchant keys is nil") - } - for _, k := range *keys { - s, err := k.GetSecretKey() - if err != nil { - return nil, fmt.Errorf("error getting key's secret value: %w", err) - } - if s == nil { - return nil, fmt.Errorf("invalid empty value for secret key") - } - resp = append(resp, []byte(*s)) - } - return resp, nil -} - -// credChunkFn - given a time, calculate the next increment of time based on interval -func credChunkFn(interval timeutils.ISODuration) func(time.Time) (time.Time, time.Time) { - return func(t time.Time) (time.Time, time.Time) { - var ( - start time.Time - end time.Time - ) - - // get the future time one credential interval away - c, err := interval.From(t) - if err != nil { - return start, end - } - // get the go duration to that future time one credential away - td := (*c).Sub(t) - - // i.e. 1 day will truncate on the day - // i.e. 1 month will truncate on the month - switch interval.String() { - case "P1M": - y, m, _ := t.Date() - // reset the date to be the first of the given month - start = time.Date(y, m, 1, 0, 0, 0, 0, time.UTC) - end = time.Date(y, m+1, 1, 0, 0, 0, 0, time.UTC) - default: - // use truncate - start = t.Truncate(td) - end = start.Add(td) - } - - return start, end - } -} - -// timeChunking - given a duration and interval size of credential, return number of credentials -// to generate, and a function that takes a start time and increments it by an appropriate amount -func timeChunking(ctx context.Context, issuerID string, timeLimitedSecret cryptography.TimeLimitedSecret, orderID, itemID uuid.UUID, issued time.Time, duration, interval timeutils.ISODuration) ([]TimeLimitedCreds, error) { - expiresAt, err := duration.From(issued) - if err != nil { - return nil, fmt.Errorf("unable to compute expiry") - } - // Add at least 5 days of grace period - *expiresAt = (*expiresAt).AddDate(0, 0, 5) - - chunkingFn := credChunkFn(interval) - - // set dEnd to today chunked - dEnd, _ := chunkingFn(time.Now()) - - var credentials []TimeLimitedCreds - var dStart time.Time - for dEnd.Before(*expiresAt) { - dStart, dEnd = chunkingFn(dEnd) - timeBasedToken, err := timeLimitedSecret.Derive( - []byte(issuerID), - dStart, - dEnd) - if err != nil { - return credentials, fmt.Errorf("error generating credentials: %w", err) - } - credentials = append(credentials, TimeLimitedCreds{ - ID: itemID, - OrderID: orderID, - IssuedAt: dStart.Format("2006-01-02"), - ExpiresAt: dEnd.Format("2006-01-02"), - Token: timeBasedToken, - }) - } - return credentials, nil -} - -// GetTimeLimitedCreds returns get an order's time limited creds. -func (s *Service) GetTimeLimitedCreds(ctx context.Context, order *Order, itemID, reqID uuid.UUID) ([]TimeLimitedCreds, int, error) { - if !order.IsPaid() || order.LastPaidAt == nil { - return nil, http.StatusBadRequest, model.Error("order is not paid, or invalid last paid at") - } - - issuedAt := order.LastPaidAt - - if order.ExpiresAt != nil { - // Check if it's past expiration, if so issue nothing. - if time.Now().After(*order.ExpiresAt) { - return nil, http.StatusBadRequest, model.Error("order has expired") - } - } - - secret, err := s.GetActiveCredentialSigningKey(ctx, order.MerchantID) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to get merchant signing key: %w", err) - } - - timeLimitedSecret := cryptography.NewTimeLimitedSecret(secret) - - item, ok := order.HasItem(itemID) - if !ok { - return nil, http.StatusBadRequest, model.Error("could not find specified item") - } - - if item.ValidForISO == nil { - return nil, http.StatusBadRequest, model.Error("order item has no valid for time") - } - - duration, err := timeutils.ParseDuration(*item.ValidForISO) - if err != nil { - return nil, http.StatusInternalServerError, model.Error("unable to parse order duration for credentials") - } - - if item.IssuanceIntervalISO == nil { - item.IssuanceIntervalISO = ptrTo("P1D") - } - - interval, err := timeutils.ParseDuration(*(item.IssuanceIntervalISO)) - if err != nil { - return nil, http.StatusInternalServerError, model.Error("unable to parse issuance interval for credentials") - } - - issuerID, err := encodeIssuerID(order.MerchantID, item.SKU) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("error encoding issuer: %w", err) - } - - credentials, err := timeChunking(ctx, issuerID, timeLimitedSecret, order.ID, item.ID, *issuedAt, *duration, *interval) - if err != nil { - return nil, http.StatusInternalServerError, fmt.Errorf("failed to derive credential chunking: %w", err) - } - - if len(credentials) == 0 { - return nil, http.StatusBadRequest, model.Error("failed to issue credentials") - } - - return credentials, http.StatusOK, nil -} - -type credential interface { - GetSku(context.Context) string - GetType(context.Context) string - GetMerchantID(context.Context) string - GetPresentation(context.Context) string -} - -// verifyCredential - given a credential, verify it. -func (s *Service) verifyCredential(ctx context.Context, cred credential, w http.ResponseWriter) *handlers.AppError { - logger := logging.Logger(ctx, "verifyCredential") - - merchant, err := merchantFromCtx(ctx) - if err != nil { - logger.Error().Err(err).Msg("failed to get the merchant from the context") - return handlers.WrapError(err, "Error getting auth merchant", http.StatusInternalServerError) - } - - logger.Debug().Str("merchant", merchant).Msg("got merchant from the context") - - caveats := caveatsFromCtx(ctx) - - if cred.GetMerchantID(ctx) != merchant { - logger.Warn(). - Str("req.MerchantID", cred.GetMerchantID(ctx)). - Str("merchant", merchant). - Msg("merchant does not match the key's merchant") - return handlers.WrapError(nil, "Verify request merchant does not match authentication", http.StatusForbidden) - } - - logger.Debug().Str("merchant", merchant).Msg("merchant matches the key's merchant") - - if caveats != nil { - if sku, ok := caveats["sku"]; ok { - if cred.GetSku(ctx) != sku { - logger.Warn(). - Str("req.SKU", cred.GetSku(ctx)). - Str("sku", sku). - Msg("sku caveat does not match") - return handlers.WrapError(nil, "Verify request sku does not match authentication", http.StatusForbidden) - } - } - } - logger.Debug().Msg("caveats validated") - - kind := cred.GetType(ctx) - switch kind { - case singleUse, timeLimitedV2: - return s.verifyBlindedTokenCredential(ctx, cred, w) - case timeLimited: - return s.verifyTimeLimitedV1Credential(ctx, cred, w) - default: - return handlers.WrapError(nil, "Unknown credential type", http.StatusBadRequest) - } -} - -// verifyBlindedTokenCredential verifies a single use or time limited v2 credential. -func (s *Service) verifyBlindedTokenCredential(ctx context.Context, req credential, w http.ResponseWriter) *handlers.AppError { - bytes, err := base64.StdEncoding.DecodeString(req.GetPresentation(ctx)) - if err != nil { - return handlers.WrapError(err, "Error in decoding presentation", http.StatusBadRequest) - } - - decodedCred := &cbr.CredentialRedemption{} - if err := json.Unmarshal(bytes, decodedCred); err != nil { - return handlers.WrapError(err, "Error in presentation formatting", http.StatusBadRequest) - } - - // Ensure that the credential being redeemed (opaque to merchant) matches the outer credential details. - issuerID, err := encodeIssuerID(req.GetMerchantID(ctx), req.GetSku(ctx)) - if err != nil { - return handlers.WrapError(err, "Error in outer merchantId or sku", http.StatusBadRequest) - } - - if issuerID != decodedCred.Issuer { - return handlers.WrapError(nil, "Error, outer merchant and sku don't match issuer", http.StatusBadRequest) - } - - return s.redeemBlindedCred(ctx, w, req.GetType(ctx), decodedCred) -} - -// verifyTimeLimitedV1Credential verifies a time limited v1 credential. -func (s *Service) verifyTimeLimitedV1Credential(ctx context.Context, req credential, w http.ResponseWriter) *handlers.AppError { - data, err := base64.StdEncoding.DecodeString(req.GetPresentation(ctx)) - if err != nil { - return handlers.WrapError(err, "Error in decoding presentation", http.StatusBadRequest) - } - - present := &tlv1CredPresentation{} - if err := json.Unmarshal(data, present); err != nil { - return handlers.WrapError(err, "Error in presentation formatting", http.StatusBadRequest) - } - - merchID := req.GetMerchantID(ctx) - - // Ensure that the credential being redeemed (opaque to merchant) matches the outer credential details. - issuerID, err := encodeIssuerID(merchID, req.GetSku(ctx)) - if err != nil { - return handlers.WrapError(err, "Error in outer merchantId or sku", http.StatusBadRequest) - } - - keys, err := s.GetCredentialSigningKeys(ctx, merchID) - if err != nil { - return handlers.WrapError(err, "failed to get merchant signing key", http.StatusInternalServerError) - } - - issuedAt, err := time.Parse("2006-01-02", present.IssuedAt) - if err != nil { - return handlers.WrapError(err, "Error parsing issuedAt", http.StatusBadRequest) - } - - expiresAt, err := time.Parse("2006-01-02", present.ExpiresAt) - if err != nil { - return handlers.WrapError(err, "Error parsing expiresAt", http.StatusBadRequest) - } - - for _, key := range keys { - timeLimitedSecret := cryptography.NewTimeLimitedSecret(key) - - verified, err := timeLimitedSecret.Verify([]byte(issuerID), issuedAt, expiresAt, present.Token) - if err != nil { - return handlers.WrapError(err, "Error in token verification", http.StatusBadRequest) - } - - if verified { - // Check against expiration time, issued time. - now := time.Now() - if now.After(expiresAt) || now.Before(issuedAt) { - return handlers.WrapError(nil, "Credentials are not valid", http.StatusForbidden) - } - - return handlers.RenderContent(ctx, "Credentials successfully verified", w, http.StatusOK) - } - } - - return handlers.WrapError(nil, "Credentials could not be verified", http.StatusForbidden) -} - -// RunSendSigningRequestJob - send the order credentials signing requests -func (s *Service) RunSendSigningRequestJob(ctx context.Context) (bool, error) { - return true, s.Datastore.SendSigningRequest(ctx, s) -} - -// TODO: Address in kafka refactor - -// RunStoreSignedOrderCredentials starts a signed order credentials consumer. -// This function creates a new signed order credentials consumer and starts processing messages. -// If the consumers errors we backoff, close the reader and restarts the consumer. -func (s *Service) RunStoreSignedOrderCredentials(ctx context.Context, backoff time.Duration) { - logger := logging.Logger(ctx, "skus.RunStoreSignedOrderCredentials") - - decoder := &SigningOrderResultDecoder{ - codec: s.codecs[kafkaSignedOrderCredsTopic], - } - - handler := &SignedOrderCredentialsHandler{ - decoder: decoder, - datastore: s.Datastore, - } - - errorHandler := &SigningOrderResultErrorHandler{ - kafkaWriter: s.kafkaWriter, - } - - run := func() (err error) { - reader, err := kafkautils.NewKafkaReader(ctx, kafkaSignedRequestReaderGroupID, kafkaSignedOrderCredsTopic) - if err != nil { - return fmt.Errorf("error creating kafka signed order credentials reader: %w", err) - } - defer func() { - closeErr := reader.Close() - if closeErr != nil { - if err != nil { - logger.Err(err).Msg("consumer error") - } - err = fmt.Errorf("error closing kafka reader: %w", errClosingResource) - } - }() - - err = kafkautils.Consume(ctx, reader, handler, errorHandler) - if err != nil { - return fmt.Errorf("consumer error: %w", err) - } - - return nil - } - - for { - select { - case <-ctx.Done(): - err := ctx.Err() - if err != nil { - logger.Err(err).Msg("error calling context") - } - return - default: - err := run() - if err != nil { - logger.Err(err).Msg("error running consumer") - sentry.CaptureException(err) - if errors.Is(err, errClosingResource) { - return - } - time.Sleep(backoff) - } - } - } -} - -// verifyIOSNotification - verify the developer notification from appstore -func (s *Service) verifyIOSNotification(ctx context.Context, txInfo *appstore.JWSTransactionDecodedPayload, renewalInfo *appstore.JWSRenewalInfoDecodedPayload) error { - if txInfo == nil || renewalInfo == nil { - return errors.New("notification has no tx or renewal") - } - - if !govalidator.IsAlphanumeric(txInfo.OriginalTransactionId) || len(txInfo.OriginalTransactionId) > 32 { - return errors.New("original transaction id should be alphanumeric and less than 32 chars") - } - - // lookup the order based on the token as externalID - o, err := s.Datastore.GetOrderByExternalID(txInfo.OriginalTransactionId) - if err != nil { - return fmt.Errorf("failed to get order from db (%s): %w", txInfo.OriginalTransactionId, err) - } - - if o == nil { - return fmt.Errorf("failed to get order from db (%s): %w", txInfo.OriginalTransactionId, errNotFound) - } - - // check if we are past the expiration date on transaction or the order was revoked - - if time.Now().After(time.Unix(0, txInfo.ExpiresDate*int64(time.Millisecond))) || - (txInfo.RevocationDate > 0 && time.Now().After(time.Unix(0, txInfo.RevocationDate*int64(time.Millisecond)))) { - // past our tx expires/renewal time - if err = s.CancelOrder(o.ID); err != nil { - return fmt.Errorf("failed to cancel subscription in skus: %w", err) - } - } else { - if err = s.RenewOrder(ctx, o.ID); err != nil { - return fmt.Errorf("failed to renew subscription in skus: %w", err) - } - } - return nil -} - -// verifyDeveloperNotification - verify the developer notification from playstore -func (s *Service) verifyDeveloperNotification(ctx context.Context, dn *DeveloperNotification) error { - // lookup the order based on the token as externalID - o, err := s.Datastore.GetOrderByExternalID(dn.SubscriptionNotification.PurchaseToken) - if err != nil { - return fmt.Errorf("failed to get order from db: %w", err) - } - - if o == nil { - return fmt.Errorf("failed to get order from db: %w", errNotFound) - } - - // have order, now validate the receipt from the notification - _, err = s.validateReceipt(ctx, &o.ID, SubmitReceiptRequestV1{ - Type: "android", - Blob: dn.SubscriptionNotification.PurchaseToken, - Package: dn.PackageName, - SubscriptionID: dn.SubscriptionNotification.SubscriptionID, - }) - if err != nil { - return fmt.Errorf("failed to validate purchase token: %w", err) - } - - switch dn.SubscriptionNotification.NotificationType { - case androidSubscriptionRenewed, - androidSubscriptionRecovered, - androidSubscriptionPurchased, - androidSubscriptionRestarted, - androidSubscriptionInGracePeriod, - androidSubscriptionPriceChangeConfirmed: - if err = s.RenewOrder(ctx, o.ID); err != nil { - return fmt.Errorf("failed to renew subscription in skus: %w", err) - } - case androidSubscriptionExpired, - androidSubscriptionRevoked, - androidSubscriptionPausedScheduleChanged, - androidSubscriptionPaused, - androidSubscriptionDeferred, - androidSubscriptionOnHold, - androidSubscriptionCanceled, - androidSubscriptionUnknown: - if err = s.CancelOrder(o.ID); err != nil { - return fmt.Errorf("failed to cancel subscription in skus: %w", err) - } - default: - return errors.New("failed to act on subscription notification") - } - - return nil -} - -// validateReceipt - perform receipt validation -func (s *Service) validateReceipt(ctx context.Context, orderID *uuid.UUID, receipt interface{}) (string, error) { - // based on the vendor call the vendor specific apis to check the status of the receipt, - if v, ok := receipt.(SubmitReceiptRequestV1); ok { - // and get back the external id - if fn, ok := receiptValidationFns[v.Type]; ok { - return fn(ctx, receipt) - } - } - - return "", errorutils.ErrNotImplemented -} - -// UpdateOrderStatusPaidWithMetadata - update the order status with metadata -func (s *Service) UpdateOrderStatusPaidWithMetadata(ctx context.Context, orderID *uuid.UUID, metadata datastore.Metadata) error { - // create a tx for use in all datastore calls - ctx, _, rollback, commit, err := datastore.GetTx(ctx, s.Datastore) - defer rollback() // doesnt hurt to rollback incase we panic - - if err != nil { - return fmt.Errorf("failed to get db transaction: %w", err) - } - - for k, v := range metadata { - if vv, ok := v.(string); ok { - if err := s.Datastore.AppendOrderMetadata(ctx, orderID, k, vv); err != nil { - return fmt.Errorf("failed to append order metadata: %w", err) - } - } - if vv, ok := v.(int); ok { - if err := s.Datastore.AppendOrderMetadataInt(ctx, orderID, k, vv); err != nil { - return fmt.Errorf("failed to append order metadata: %w", err) - } - } - } - if err := s.Datastore.SetOrderPaid(ctx, orderID); err != nil { - return fmt.Errorf("failed to set order paid: %w", err) - } - - return commit() -} - -func (s *Service) CreateOrder(ctx context.Context, req *model.CreateOrderRequestNew) (*Order, error) { - items, err := createOrderItems(req) - if err != nil { - return nil, err - } - - // Check for number of items to be above 0. - // - // Validation should already have taken care of this. - // This method does not know about it, hence the explicit check. - nitems := len(items) - if nitems == 0 { - return nil, model.ErrInvalidOrderRequest - } - - const merchID = "brave.com" - - tx, err := s.Datastore.RawDB().Beginx() - if err != nil { - return nil, err - } - defer func() { _ = tx.Rollback() }() - - numIntervals, err := s.createOrderIssuers(ctx, tx, merchID, items) - if err != nil { - return nil, err - } - - oreq := &model.OrderNew{ - MerchantID: merchID, - Currency: req.Currency, - Status: model.OrderStatusPending, - TotalPrice: model.OrderItemList(items).TotalCost(), - AllowedPaymentMethods: pq.StringArray(req.PaymentMethods), - } - - if oreq.TotalPrice.IsZero() { - oreq.Status = model.OrderStatusPaid - } - - // Location on the order is only defined when there is only one item. - // - // Multi-item orders have NULL location. - if nitems == 1 && items[0].Location.Valid { - oreq.Location.Valid = true - oreq.Location.String = items[0].Location.String - } - - { - // Use validFor from the first item. - // - // TODO: Deprecate the use of valid_for: - // valid_for_iso is now used instead of valid_for for calculating order's expiration time. - // - // The old code in CreateOrderFromRequest does a contradictory thing – it takes validFor from last item. - // It does not make any sense, but it's working because there is only one item normally. - var vf time.Duration - if items[0].ValidFor != nil { - vf = *items[0].ValidFor - } - - oreq.ValidFor = &vf - } - - order, err := s.Datastore.CreateOrder(ctx, tx, oreq, items) - if err != nil { - return nil, fmt.Errorf("failed to create order: %w", err) - } - - if err := tx.Commit(); err != nil { - return nil, err - } - - if !order.IsPaid() { - switch { - case order.IsStripePayable(): - ssid, err := s.createStripeSessID(ctx, req, order) - if err != nil { - return nil, err - } - - if err := s.Datastore.AppendOrderMetadata(ctx, &order.ID, "stripeCheckoutSessionId", ssid); err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - - // Backporting this from the legacy method CreateOrderFromRequest. - case order.IsRadomPayable(): - ssid, err := s.createRadomSessID(ctx, req, order) - if err != nil { - return nil, fmt.Errorf("failed to create checkout session: %w", err) - } - - if err := s.Datastore.AppendOrderMetadata(ctx, &order.ID, "radomCheckoutSessionId", ssid); err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - } - - if numIntervals > 0 { - if err := s.Datastore.AppendOrderMetadataInt(ctx, &order.ID, "numIntervals", numIntervals); err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - - // Backporting changes from https://github.com/brave-intl/bat-go/pull/1998. - { - numPerInterval := 2 - if nitems == 1 && items[0].IsLeo() { - numPerInterval = 192 - } - - if err := s.Datastore.AppendOrderMetadataInt(ctx, &order.ID, "numPerInterval", numPerInterval); err != nil { - return nil, fmt.Errorf("failed to update order metadata: %w", err) - } - } - - return order, nil -} - -// createOrderIssuers checks that the issuer exists for the item's product. -// -// TODO: Remove this when products & issuers have been reworked. -// The issuer for a product must be created when the product is created. -func (s *Service) createOrderIssuers(ctx context.Context, dbi sqlx.QueryerContext, merchID string, items []model.OrderItem) (int, error) { - var numIntervals int - for i := range items { - switch items[i].CredentialType { - case singleUse: - if err := s.CreateIssuer(ctx, dbi, merchID, &items[i]); err != nil { - return 0, errorutils.Wrap(err, "error finding issuer") - } - case timeLimitedV2: - if err := s.CreateIssuerV3(ctx, dbi, merchID, &items[i], *items[i].IssuerConfig); err != nil { - const msg = "error creating issuer for merchantID %s and sku %s: %w" - return 0, fmt.Errorf(msg, merchID, items[i].SKU, err) - } - - numIntervals = items[i].IssuerConfig.NumIntervals() - } - } - - return numIntervals, nil -} - -func (s *Service) createStripeSessID(ctx context.Context, req *model.CreateOrderRequestNew, order *model.Order) (string, error) { - oid := order.ID.String() - - surl, err := req.StripeMetadata.SuccessURL(oid) - if err != nil { - return "", err - } - - curl, err := req.StripeMetadata.CancelURL(oid) - if err != nil { - return "", err - } - - sess, err := model.CreateStripeCheckoutSession(oid, req.Email, surl, curl, order.GetTrialDays(), order.Items) - if err != nil { - return "", fmt.Errorf("failed to create checkout session: %w", err) - } - - return sess.SessionID, nil -} - -// TODO: Refactor the Radom-related logic. -func (s *Service) createRadomSessID(ctx context.Context, req *model.CreateOrderRequestNew, order *model.Order) (string, error) { - sess, err := order.CreateRadomCheckoutSession(ctx, s.radomClient, s.radomSellerAddress) - if err != nil { - return "", err - } - - return sess.SessionID, nil -} - -func (s *Service) redeemBlindedCred(ctx context.Context, w http.ResponseWriter, kind string, cred *cbr.CredentialRedemption) *handlers.AppError { - var redeemFn func(ctx context.Context, issuer, preimage, signature, payload string) error - - switch kind { - case singleUse: - redeemFn = s.cbClient.RedeemCredential - case timeLimitedV2: - redeemFn = s.cbClient.RedeemCredentialV3 - default: - return handlers.WrapError(fmt.Errorf("credential type %s not suppoted", kind), "unknown credential type %s", http.StatusBadRequest) - } - - // FIXME: we shouldn't be using the issuer as the payload, it ideally would be a unique request identifier - // to allow for more flexible idempotent behavior. - if err := redeemFn(ctx, cred.Issuer, cred.TokenPreimage, cred.Signature, cred.Issuer); err != nil { - msg := err.Error() - - // Time limited v2: Expose a credential id so the caller can decide whether to allow multiple redemptions. - if kind == timeLimitedV2 && msg == cbr.ErrDupRedeem.Error() { - data := &blindedCredVrfResult{ID: cred.TokenPreimage, Duplicate: true} - - return handlers.RenderContent(ctx, data, w, http.StatusOK) - } - - // Duplicate redemptions are not verified. - if msg == cbr.ErrDupRedeem.Error() || msg == cbr.ErrBadRequest.Error() { - return handlers.WrapError(err, "invalid credentials", http.StatusForbidden) - } - - return handlers.WrapError(err, "Error verifying credentials", http.StatusInternalServerError) - } - - // TODO(clD11): cleanup after quick fix - if kind == timeLimitedV2 { - return handlers.RenderContent(ctx, &blindedCredVrfResult{ID: cred.TokenPreimage}, w, http.StatusOK) - } - return handlers.RenderContent(ctx, "Credentials successfully verified", w, http.StatusOK) -} - -func createOrderItems(req *model.CreateOrderRequestNew) ([]model.OrderItem, error) { - result := make([]model.OrderItem, 0) - - for i := range req.Items { - item, err := createOrderItem(&req.Items[i]) - if err != nil { - return nil, err - } - - item.Currency = req.Currency - - result = append(result, *item) - } - - return result, nil -} - -func createOrderItem(req *model.OrderItemRequestNew) (*model.OrderItem, error) { - if req.CredentialValidDurationEach != nil { - if _, err := timeutils.ParseDuration(*req.CredentialValidDurationEach); err != nil { - return nil, err - } - } - - validFor, err := durationFromISO(req.CredentialValidDuration) - if err != nil { - return nil, err - } - - result := &model.OrderItem{ - SKU: req.SKU, - // Set Currency separately as it should be at the Order level. - CredentialType: req.CredentialType, - ValidFor: &validFor, - ValidForISO: &req.CredentialValidDuration, - EachCredentialValidForISO: req.CredentialValidDurationEach, - IssuanceIntervalISO: req.IssuanceInterval, - - Price: req.Price, - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: req.Location, - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: req.Description, - }, - }, - Quantity: req.Quantity, - Metadata: req.StripeMetadata.Metadata(), - Subtotal: req.Price.Mul(decimal.NewFromInt(int64(req.Quantity))), - IssuerConfig: &model.IssuerConfig{ - Buffer: req.TokenBufferOrDefault(), - Overlap: req.TokenOverlapOrDefault(), - }, - } - - return result, nil -} - -func durationFromISO(v string) (time.Duration, error) { - dur, err := timeutils.ParseDuration(v) - if err != nil { - return 0, err - } - - durt, err := dur.FromNow() - if err != nil { - return 0, err - } - - return time.Until(*durt), nil -} - -type blindedCredVrfResult struct { - ID string `json:"id"` - Duplicate bool `json:"duplicate"` -} - -type tlv1CredPresentation struct { - Token string `json:"token"` - IssuedAt string `json:"issuedAt"` - ExpiresAt string `json:"expiresAt"` -} - -func ptrTo[T any](v T) *T { - return &v -} diff --git a/services/skus/service_nonint_test.go b/services/skus/service_nonint_test.go deleted file mode 100644 index 8f182b5f8..000000000 --- a/services/skus/service_nonint_test.go +++ /dev/null @@ -1,381 +0,0 @@ -package skus - -import ( - "testing" - - uuid "github.com/satori/go.uuid" - should "github.com/stretchr/testify/assert" - - "github.com/brave-intl/bat-go/libs/datastore" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -func TestCheckNumBlindedCreds(t *testing.T) { - type tcGiven struct { - ord *model.Order - item *model.OrderItem - ncreds int - } - - type testCase struct { - name string - given tcGiven - exp error - } - - tests := []testCase{ - { - name: "irrelevant_credential_type", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimited, - }, - }, - }, - - { - name: "single_use_valid_1", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: singleUse, - Quantity: 1, - }, - ncreds: 1, - }, - }, - - { - name: "single_use_valid_2", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: singleUse, - Quantity: 2, - }, - ncreds: 1, - }, - }, - - { - name: "single_use_invalid", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: singleUse, - Quantity: 2, - }, - ncreds: 3, - }, - exp: errInvalidNCredsSingleUse, - }, - - { - name: "tlv2_invalid_numPerInterval_missing", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{}, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 6, - }, - exp: model.ErrNumPerIntervalNotSet, - }, - - { - name: "tlv2_invalid_numPerInterval_invalid", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{ - "numPerInterval": "NaN", - }, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 6, - }, - exp: model.ErrInvalidNumPerInterval, - }, - - { - name: "tlv2_invalid_numIntervals_missing", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{ - // We get a float64 upon fetching from the database. - "numPerInterval": float64(2), - }, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 6, - }, - exp: model.ErrNumIntervalsNotSet, - }, - - { - name: "tlv2_invalid_numIntervals_invalid", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{ - // We get a float64 upon fetching from the database. - "numPerInterval": float64(2), - "numIntervals": "NaN", - }, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 6, - }, - exp: model.ErrInvalidNumIntervals, - }, - - { - name: "tlv2_valid_1", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{ - // We get a float64 upon fetching from the database. - "numPerInterval": float64(2), - "numIntervals": float64(3), - }, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 6, - }, - }, - - { - name: "tlv2_valid_2", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{ - // We get a float64 upon fetching from the database. - "numPerInterval": float64(2), - "numIntervals": float64(4), - }, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 6, - }, - }, - - { - name: "tlv2_invalid", - given: tcGiven{ - ord: &model.Order{ - ID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - Metadata: datastore.Metadata{ - // We get a float64 upon fetching from the database. - "numPerInterval": float64(2), - "numIntervals": float64(3), - }, - }, - item: &model.OrderItem{ - ID: uuid.Must(uuid.FromString("82514074-c4f5-4515-8d8d-29ab943615b3")), - OrderID: uuid.Must(uuid.FromString("df140c71-740b-46c9-bedd-27be0b1e6354")), - CredentialType: timeLimitedV2, - Quantity: 1, - }, - ncreds: 7, - }, - exp: errInvalidNCredsTlv2, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - actual := checkNumBlindedCreds(tc.given.ord, tc.given.item, tc.given.ncreds) - - should.Equal(t, tc.exp, actual) - }) - } -} - -func TestDoItemsHaveSUOrTlv2(t *testing.T) { - type testCase struct { - name string - given []model.OrderItem - expSU bool - expTlv2 bool - } - - tests := []testCase{ - { - name: "nil", - }, - - { - name: "empty", - given: []model.OrderItem{}, - }, - - { - name: "one_single_use", - given: []model.OrderItem{ - { - CredentialType: singleUse, - }, - }, - expSU: true, - }, - - { - name: "two_single_use", - given: []model.OrderItem{ - { - CredentialType: singleUse, - }, - - { - CredentialType: singleUse, - }, - }, - expSU: true, - }, - - { - name: "one_time_limited", - given: []model.OrderItem{ - { - CredentialType: timeLimited, - }, - }, - }, - - { - name: "two_time_limited", - given: []model.OrderItem{ - { - CredentialType: timeLimited, - }, - - { - CredentialType: timeLimited, - }, - }, - }, - - { - name: "one_time_limited_v2", - given: []model.OrderItem{ - { - CredentialType: timeLimitedV2, - }, - }, - expTlv2: true, - }, - - { - name: "two_time_limited_v2", - given: []model.OrderItem{ - { - CredentialType: timeLimitedV2, - }, - - { - CredentialType: timeLimitedV2, - }, - }, - expTlv2: true, - }, - - { - name: "one_single_use_one_time_limited_v2", - given: []model.OrderItem{ - { - CredentialType: singleUse, - }, - - { - CredentialType: timeLimitedV2, - }, - }, - expSU: true, - expTlv2: true, - }, - - { - name: "all_one", - given: []model.OrderItem{ - { - CredentialType: singleUse, - }, - - { - CredentialType: timeLimited, - }, - - { - CredentialType: timeLimitedV2, - }, - }, - expSU: true, - expTlv2: true, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - doSingleUse, doTlv2 := doItemsHaveSUOrTlv2(tc.given) - - should.Equal(t, tc.expSU, doSingleUse) - should.Equal(t, tc.expTlv2, doTlv2) - }) - } -} diff --git a/services/skus/service_test.go b/services/skus/service_test.go deleted file mode 100644 index 683203ba0..000000000 --- a/services/skus/service_test.go +++ /dev/null @@ -1,308 +0,0 @@ -//go:build integration - -package skus - -import ( - "database/sql" - "errors" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/ptr" - "github.com/shopspring/decimal" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/libs/datastore" - timeutils "github.com/brave-intl/bat-go/libs/time" - "github.com/brave-intl/bat-go/services/skus/model" -) - -func TestCredChunkFn(t *testing.T) { - // Jan 1, 2021 - issued := time.Date(2021, time.January, 20, 0, 0, 0, 0, time.UTC) - - // 1 day - day, err := timeutils.ParseDuration("P1D") - if err != nil { - t.Errorf("failed to parse 1 day: %s", err.Error()) - } - - // 1 month - mo, err := timeutils.ParseDuration("P1M") - if err != nil { - t.Errorf("failed to parse 1 month: %s", err.Error()) - } - - this, next := credChunkFn(*day)(issued) - if this.Day() != 20 { - t.Errorf("day - the next day should be 2") - } - if this.Month() != 1 { - t.Errorf("day - the next month should be 1") - } - if next.Day() != 21 { - t.Errorf("day - the next day should be 2") - } - if next.Month() != 1 { - t.Errorf("day - the next month should be 1") - } - - this, next = credChunkFn(*mo)(issued) - if this.Day() != 1 { - t.Errorf("mo - the next day should be 1") - } - if this.Month() != 1 { - t.Errorf("mo - the next month should be 2") - } - if next.Day() != 1 { - t.Errorf("mo - the next day should be 1") - } - if next.Month() != 2 { - t.Errorf("mo - the next month should be 2") - } -} - -func TestCreateOrderItems(t *testing.T) { - type tcExpected struct { - result []model.OrderItem - err error - } - - type testCase struct { - name string - given *model.CreateOrderRequestNew - exp tcExpected - } - - tests := []testCase{ - { - name: "empty", - given: &model.CreateOrderRequestNew{}, - exp: tcExpected{ - result: []model.OrderItem{}, - }, - }, - - { - name: "invalid_CredentialValidDurationEach", - given: &model.CreateOrderRequestNew{ - Items: []model.OrderItemRequestNew{ - { - CredentialValidDuration: "P1M", - CredentialValidDurationEach: ptr.To("rubbish"), - }, - }, - }, - exp: tcExpected{ - err: timeutils.ErrUnsupportedFormat, - }, - }, - - { - name: "ensure_currency_set", - given: &model.CreateOrderRequestNew{ - Currency: "USD", - Items: []model.OrderItemRequestNew{ - { - Location: "location", - Description: "description", - CredentialValidDuration: "P1M", - }, - - { - Location: "location", - Description: "description", - CredentialValidDuration: "P1M", - }, - }, - }, - exp: tcExpected{ - result: []model.OrderItem{ - { - Currency: "USD", - ValidForISO: ptr.To("P1M"), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "description", - }, - }, - IssuerConfig: &model.IssuerConfig{ - Buffer: 30, - Overlap: 5, - }, - Subtotal: decimal.NewFromInt(0), - }, - - { - Currency: "USD", - ValidForISO: ptr.To("P1M"), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "description", - }, - }, - - IssuerConfig: &model.IssuerConfig{ - Buffer: 30, - Overlap: 5, - }, - Subtotal: decimal.NewFromInt(0), - }, - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act, err := createOrderItems(tc.given) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - must.Equal(t, len(tc.exp.result), len(act)) - - // Override ValidFor because it's not deterministic. - for j := range act { - tc.exp.result[j].ValidFor = act[j].ValidFor - } - - should.Equal(t, tc.exp.result, act) - }) - } -} - -func TestCreateOrderItem(t *testing.T) { - type tcExpected struct { - result *model.OrderItem - err error - } - - type testCase struct { - name string - given *model.OrderItemRequestNew - exp tcExpected - } - - tests := []testCase{ - { - name: "invalid_CredentialValidDurationEach", - given: &model.OrderItemRequestNew{ - CredentialValidDurationEach: ptr.To("rubbish"), - }, - exp: tcExpected{ - err: timeutils.ErrUnsupportedFormat, - }, - }, - - { - name: "invalid_CredentialValidDuration", - given: &model.OrderItemRequestNew{ - CredentialValidDuration: "rubbish", - CredentialValidDurationEach: ptr.To("P1M"), - }, - exp: tcExpected{ - err: timeutils.ErrUnsupportedFormat, - }, - }, - - { - name: "full_example", - given: &model.OrderItemRequestNew{ - SKU: "sku", - CredentialType: "credential_type", - CredentialValidDuration: "P1M", - CredentialValidDurationEach: ptr.To("P1D"), - IssuanceInterval: ptr.To("P1M"), - Price: decimal.NewFromInt(10), - Location: "location", - Description: "description", - Quantity: 2, - StripeMetadata: &model.ItemStripeMetadata{ - ProductID: "product_id", - ItemID: "item_id", - }, - IssuerTokenBuffer: 10, - }, - exp: tcExpected{ - result: &model.OrderItem{ - SKU: "sku", - CredentialType: "credential_type", - ValidFor: mustDurationFromISO("P1M"), - ValidForISO: ptr.To("P1M"), - EachCredentialValidForISO: ptr.To("P1D"), - IssuanceIntervalISO: ptr.To("P1M"), - Price: decimal.NewFromInt(10), - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "location", - }, - }, - Description: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "description", - }, - }, - Quantity: 2, - Metadata: map[string]interface{}{ - "stripe_product_id": "product_id", - "stripe_item_id": "item_id", - }, - Subtotal: decimal.NewFromInt(10).Mul(decimal.NewFromInt(int64(2))), - IssuerConfig: &model.IssuerConfig{ - Buffer: 10, - Overlap: 5, - }, - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act, err := createOrderItem(tc.given) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - // Override ValidFor because it's not deterministic. - tc.exp.result.ValidFor = act.ValidFor - - should.Equal(t, tc.exp.result, act) - }) - } -} - -func mustDurationFromISO(v string) *time.Duration { - result, err := durationFromISO(v) - if err != nil { - panic(err) - } - - return &result -} diff --git a/services/skus/skus.go b/services/skus/skus.go deleted file mode 100644 index 057a7fa4b..000000000 --- a/services/skus/skus.go +++ /dev/null @@ -1,125 +0,0 @@ -package skus - -import ( - "context" - "fmt" - - appctx "github.com/brave-intl/bat-go/libs/context" -) - -const ( - prodUserWalletVote = "AgEJYnJhdmUuY29tAiNicmF2ZSB1c2VyLXdhbGxldC12b3RlIHNrdSB0b2tlbiB2MQACFHNrdT11c2VyLXdhbGxldC12b3RlAAIKcHJpY2U9MC4yNQACDGN1cnJlbmN5PUJBVAACDGRlc2NyaXB0aW9uPQACGmNyZWRlbnRpYWxfdHlwZT1zaW5nbGUtdXNlAAAGIOaNAUCBMKm0IaLqxefhvxOtAKB0OfoiPn0NPVfI602J" - prodAnonCardVote = "AgEJYnJhdmUuY29tAiFicmF2ZSBhbm9uLWNhcmQtdm90ZSBza3UgdG9rZW4gdjEAAhJza3U9YW5vbi1jYXJkLXZvdGUAAgpwcmljZT0wLjI1AAIMY3VycmVuY3k9QkFUAAIMZGVzY3JpcHRpb249AAIaY3JlZGVudGlhbF90eXBlPXNpbmdsZS11c2UAAAYgrMZm85YYwnmjPXcegy5pBM5C+ZLfrySZfYiSe13yp8o=" - prodBraveTogetherPaid = "MDAyMGxvY2F0aW9uIHRvZ2V0aGVyLmJyYXZlLmNvbQowMDMwaWRlbnRpZmllciBicmF2ZS10b2dldGhlci1wYWlkIHNrdSB0b2tlbiB2MQowMDIwY2lkIHNrdT1icmF2ZS10b2dldGhlci1wYWlkCjAwMTBjaWQgcHJpY2U9NQowMDE1Y2lkIGN1cnJlbmN5PVVTRAowMDQzY2lkIGRlc2NyaXB0aW9uPU9uZSBtb250aCBwYWlkIHN1YnNjcmlwdGlvbiBmb3IgQnJhdmUgVG9nZXRoZXIKMDAyNWNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyZnNpZ25hdHVyZSAl/eGfP93lrklACcFClNPvkP3Go0HCtfYVQMs5n/NJpgo=" - - prodBraveTalkPremiumTimeLimited = "MDAxY2xvY2F0aW9uIHRhbGsuYnJhdmUuY29tCjAwNDFpZGVudGlmaWVyIGJyYXZlLXRhbGstcHJlbWl1bS1wcm9kIHRpbWUgbGltaXRlZCBza3UgdG9rZW4gdjEKMDAxZmNpZCBza3U9YnJhdmUtdGFsay1wcmVtaXVtCjAwMTNjaWQgcHJpY2U9Ny4wMAowMDE1Y2lkIGN1cnJlbmN5PVVTRAowMDMxY2lkIGRlc2NyaXB0aW9uPVByZW1pdW0gYWNjZXNzIHRvIEJyYXZlIFRhbGsKMDAyNWNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyN2NpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1zdHJpcGUKMDEwYmNpZCBtZXRhZGF0YT0geyAic3RyaXBlX3Byb2R1Y3RfaWQiOiAicHJvZF9KdzR6UXhkSGtweFNPZSIsICJzdHJpcGVfaXRlbV9pZCI6ICJwcmljZV8xSklDcEVCU20xbXRyTjlud0NLdnBZUTQiLCAic3RyaXBlX3N1Y2Nlc3NfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5jb20vYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuY29tL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSBO3HtH7rpK5LFD9LIj4m1WGcPjxGO5T3msNCNlySS+QAo=" - prodBraveSearchYearPremiumTimeLimited = "MDAxZWxvY2F0aW9uIHNlYXJjaC5icmF2ZS5jb20KMDAzMWlkZW50aWZpZXIgYnJhdmUtc2VhcmNoLXByZW1pdW0gc2t1IHRva2VuIHYxCjAwMjFjaWQgc2t1PWJyYXZlLXNlYXJjaC1wcmVtaXVtCjAwMTRjaWQgcHJpY2U9MzAuMDAKMDAxNWNpZCBjdXJyZW5jeT1VU0QKMDAzM2NpZCBkZXNjcmlwdGlvbj1QcmVtaXVtIGFjY2VzcyB0byBCcmF2ZSBTZWFyY2gKMDAyNWNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMVkKMDAxZWNpZCBpc3N1YW5jZV9pbnRlcnZhbD1QMU0KMDAyN2NpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1zdHJpcGUKMDExNWNpZCBtZXRhZGF0YT0geyAic3RyaXBlX3Byb2R1Y3RfaWQiOiAicHJvZF9LVGx5emVjc3E3ZXZrNiIsICJzdHJpcGVfaXRlbV9pZCI6ICJwcmljZV8xSm9vUjhCU20xbXRyTjlubWMydmJUMDciLCAic3RyaXBlX3N1Y2Nlc3NfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInN0cmlwZV9jYW5jZWxfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9wbGFucy8/aW50ZW50PWNoZWNrb3V0IiB9CjAwMmZzaWduYXR1cmUg67IJ+1vENMQjtY96hAj+rfAqPcmxTuxJXzMogrbAK/IK" - prodBraveSearchPremiumTimeLimited = "MDAxZWxvY2F0aW9uIHNlYXJjaC5icmF2ZS5jb20KMDAzMWlkZW50aWZpZXIgYnJhdmUtc2VhcmNoLXByZW1pdW0gc2t1IHRva2VuIHYxCjAwMjFjaWQgc2t1PWJyYXZlLXNlYXJjaC1wcmVtaXVtCjAwMTNjaWQgcHJpY2U9My4wMAowMDE1Y2lkIGN1cnJlbmN5PVVTRAowMDMzY2lkIGRlc2NyaXB0aW9uPVByZW1pdW0gYWNjZXNzIHRvIEJyYXZlIFNlYXJjaAowMDI1Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDFlY2lkIGlzc3VhbmNlX2ludGVydmFsPVAxTQowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTBiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0tUbHl6ZWNzcTdldms2IiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFKb29RbkJTbTFtdHJOOW5uMk9NS3BqaiIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLmNvbS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInN0cmlwZV9jYW5jZWxfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIK0QiErbDD+400vJNO6g2ijcF/5uh7C9RuRvg2q3IFw8Cg==" - prodBraveFirewallVPNPremiumTimeLimitedV2 = "MDAxYmxvY2F0aW9uIHZwbi5icmF2ZS5jb20KMDAyMWlkZW50aWZpZXIgYnJhdmUtdnBuLXByZW1pdW0KMDAxZWNpZCBza3U9YnJhdmUtdnBuLXByZW1pdW0KMDAxM2NpZCBwcmljZT05Ljk5CjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMjZjaWQgZGVzY3JpcHRpb249YnJhdmUtdnBuLXByZW1pdW0KMDAyOGNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkLXYyCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyYmNpZCBlYWNoX2NyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFECjAwMWFjaWQgZXhwaXJlc19hZnRlcj1QMU0KMDAxZmNpZCBpc3N1ZXJfdG9rZW5fYnVmZmVyPTMxCjAwMWZjaWQgaXNzdWVyX3Rva2VuX292ZXJsYXA9MgowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTBiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0xodjhxc1BzbjZXSHJ4IiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFMMFZIbUJTbTFtdHJOOW5UNURQbVVaYiIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLmNvbS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInN0cmlwZV9jYW5jZWxfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIA6wxaFI2HqlTuX+wPorRuUIp4pQv++J1xAMATTnV6kzCg==" - prodBraveFirewallVPNPremiumTimeLimitedV2BAT = "MDAxYmxvY2F0aW9uIHZwbi5icmF2ZS5jb20KMDAyMWlkZW50aWZpZXIgYnJhdmUtdnBuLXByZW1pdW0KMDAxZWNpZCBza3U9YnJhdmUtdnBuLXByZW1pdW0KMDAxMWNpZCBwcmljZT0xNQowMDE1Y2lkIGN1cnJlbmN5PUJBVAowMDI2Y2lkIGRlc2NyaXB0aW9uPWJyYXZlLXZwbi1wcmVtaXVtCjAwMjhjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZC12MgowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMmJjaWQgZWFjaF9jcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxRAowMDFhY2lkIGV4cGlyZXNfYWZ0ZXI9UDFNCjAwMWZjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zMQowMDFmY2lkIGlzc3Vlcl90b2tlbl9vdmVybGFwPTIKMDAyNmNpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1yYWRvbQowMGQ0Y2lkIG1ldGFkYXRhPSB7ICJyYWRvbV9wcm9kdWN0X2lkIjogInByb2RfTGh2OHFzUHNuNldIcngiLCAicmFkb21fc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLmNvbS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInJhZG9tX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLmNvbS9wbGFucy8/aW50ZW50PWNoZWNrb3V0IiB9CjAwMmZzaWduYXR1cmUghrNnKGx/369LtfDHdt9u4aorHf9DW2Sq/E9Ou9+jeP8K" - - prodBraveLeoPremiumTimeLimitedV2 = "MDAxYmxvY2F0aW9uIGxlby5icmF2ZS5jb20KMDAyMWlkZW50aWZpZXIgYnJhdmUtbGVvLXByZW1pdW0KMDAxZWNpZCBza3U9YnJhdmUtbGVvLXByZW1pdW0KMDAxNGNpZCBwcmljZT0xNS4wMAowMDE1Y2lkIGN1cnJlbmN5PVVTRAowMDI2Y2lkIGRlc2NyaXB0aW9uPWJyYXZlLWxlby1wcmVtaXVtCjAwMjhjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZC12MgowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMmJjaWQgZWFjaF9jcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxRAowMDFhY2lkIGV4cGlyZXNfYWZ0ZXI9UDFNCjAwMWVjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zCjAwMWZjaWQgaXNzdWVyX3Rva2VuX292ZXJsYXA9MAowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTBiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX085dUtEWXNSUFhOZ2ZCIiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFOWG1qMEJTbTFtdHJOOW5GMGVsSWhpcSIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLmNvbS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInN0cmlwZV9jYW5jZWxfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIHToZKM6hZXoDiPlcojcpHpCBtBl4hPQ5JjGaCzvFInRCg==" - prodBraveLeoYearlyPremiumTimeLimitedV2 = "MDAxYmxvY2F0aW9uIGxlby5icmF2ZS5jb20KMDAyNmlkZW50aWZpZXIgYnJhdmUtbGVvLXByZW1pdW0teWVhcgowMDIzY2lkIHNrdT1icmF2ZS1sZW8tcHJlbWl1bS15ZWFyCjAwMTVjaWQgcHJpY2U9MTM1LjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMjZjaWQgZGVzY3JpcHRpb249YnJhdmUtbGVvLXByZW1pdW0KMDAyOGNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkLXYyCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMVkKMDAyYmNpZCBlYWNoX2NyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFECjAwMWFjaWQgZXhwaXJlc19hZnRlcj1QMU0KMDAxZWNpZCBpc3N1ZXJfdG9rZW5fYnVmZmVyPTMKMDAxZmNpZCBpc3N1ZXJfdG9rZW5fb3ZlcmxhcD0wCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMGJjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfTzl1S0RZc1JQWE5nZkIiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMU5YbWZUQlNtMW10ck45bnlibnlvbElkIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuY29tL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLmNvbS9wbGFucy8/aW50ZW50PWNoZWNrb3V0IiB9CjAwMmZzaWduYXR1cmUgC1sM6+U3xaQNwC6+ix4MMAfbtw4Gc/Dx4B6MpOLFL+YK" - - stagingBraveLeoPremiumTimeLimitedV2 = "MDAyM2xvY2F0aW9uIGxlby5icmF2ZXNvZnR3YXJlLmNvbQowMDIxaWRlbnRpZmllciBicmF2ZS1sZW8tcHJlbWl1bQowMDFlY2lkIHNrdT1icmF2ZS1sZW8tcHJlbWl1bQowMDE0Y2lkIHByaWNlPTE1LjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMjZjaWQgZGVzY3JpcHRpb249YnJhdmUtbGVvLXByZW1pdW0KMDAyOGNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkLXYyCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyYmNpZCBlYWNoX2NyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFECjAwMWVjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zCjAwMWZjaWQgaXNzdWVyX3Rva2VuX292ZXJsYXA9MAowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTFiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX09LUllKNzd3WU9rNzcxIiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFOWG1mVEJTbTFtdHJOOW5ZalNOTXM0WCIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSB3jKgiznLS0q2Y3dS1fWHxfywUOe8JHM3J1QJ1Xkqi3go=" - stagingBraveLeoYearlyPremiumTimeLimitedV2 = "MDAyM2xvY2F0aW9uIGxlby5icmF2ZXNvZnR3YXJlLmNvbQowMDI2aWRlbnRpZmllciBicmF2ZS1sZW8tcHJlbWl1bS15ZWFyCjAwMjNjaWQgc2t1PWJyYXZlLWxlby1wcmVtaXVtLXllYXIKMDAxNWNpZCBwcmljZT0xMzUuMDAKMDAxNWNpZCBjdXJyZW5jeT1VU0QKMDAyNmNpZCBkZXNjcmlwdGlvbj1icmF2ZS1sZW8tcHJlbWl1bQowMDI4Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQtdjIKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxWQowMDJiY2lkIGVhY2hfY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMUQKMDAxZWNpZCBpc3N1ZXJfdG9rZW5fYnVmZmVyPTMKMDAxZmNpZCBpc3N1ZXJfdG9rZW5fb3ZlcmxhcD0wCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMWJjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfT0tSWUo3N3dZT2s3NzEiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMU5YbWZUQlNtMW10ck45bnlibnlvbElkIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlINmyt2X+i2RrTovEz5/8hkHucz1eso6YSnYZZlUlY9uvCg==" - - stagingUserWalletVote = "AgEJYnJhdmUuY29tAiNicmF2ZSB1c2VyLXdhbGxldC12b3RlIHNrdSB0b2tlbiB2MQACFHNrdT11c2VyLXdhbGxldC12b3RlAAIKcHJpY2U9MC4yNQACDGN1cnJlbmN5PUJBVAACDGRlc2NyaXB0aW9uPQACGmNyZWRlbnRpYWxfdHlwZT1zaW5nbGUtdXNlAAAGIOH4Li+rduCtFOfV8Lfa2o8h4SQjN5CuIwxmeQFjOk4W" - stagingAnonCardVote = "AgEJYnJhdmUuY29tAiFicmF2ZSBhbm9uLWNhcmQtdm90ZSBza3UgdG9rZW4gdjEAAhJza3U9YW5vbi1jYXJkLXZvdGUAAgpwcmljZT0wLjI1AAIMY3VycmVuY3k9QkFUAAIMZGVzY3JpcHRpb249AAIaY3JlZGVudGlhbF90eXBlPXNpbmdsZS11c2UAAAYgPV/WYY5pXhodMPvsilnrLzNH6MA8nFXwyg0qSWX477M=" - stagingWebtestPJSKUDemo = "AgEYd2VidGVzdC1wai5oZXJva3VhcHAuY29tAih3ZWJ0ZXN0LXBqLmhlcm9rdWFwcC5jb20gYnJhdmUtdHNoaXJ0IHYxAAIQc2t1PWJyYXZlLXRzaGlydAACCnByaWNlPTAuMjUAAgxjdXJyZW5jeT1CQVQAAgxkZXNjcmlwdGlvbj0AAhpjcmVkZW50aWFsX3R5cGU9c2luZ2xlLXVzZQAABiCcJ0zXGbSg+s3vsClkci44QQQTzWJb9UPyJASMVU11jw==" - - stagingBraveSearchPremiumTimeLimited = "MDAyNmxvY2F0aW9uIHNlYXJjaC5icmF2ZXNvZnR3YXJlLmNvbQowMDMxaWRlbnRpZmllciBicmF2ZS1zZWFyY2gtcHJlbWl1bSBza3UgdG9rZW4gdjEKMDAyMWNpZCBza3U9YnJhdmUtc2VhcmNoLXByZW1pdW0KMDAxM2NpZCBwcmljZT0zLjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMzNjaWQgZGVzY3JpcHRpb249UHJlbWl1bSBhY2Nlc3MgdG8gQnJhdmUgU2VhcmNoCjAwMjVjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZAowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMWVjaWQgaXNzdWFuY2VfaW50ZXJ2YWw9UDFNCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMWJjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfS1RtNkphWnNzQU5QQnYiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMUpvb1hyQlNtMW10ck45bjNtUklMZVhNIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIKgf59ZBTJMyykzMrRbXaimDbL26csEeNOlcZ0EMUbBsCg==" - stagingBraveSearchYearPremiumTimeLimited = "MDAyNmxvY2F0aW9uIHNlYXJjaC5icmF2ZXNvZnR3YXJlLmNvbQowMDMxaWRlbnRpZmllciBicmF2ZS1zZWFyY2gtcHJlbWl1bSBza3UgdG9rZW4gdjEKMDAyMWNpZCBza3U9YnJhdmUtc2VhcmNoLXByZW1pdW0KMDAxNGNpZCBwcmljZT0zMC4wMAowMDE1Y2lkIGN1cnJlbmN5PVVTRAowMDMzY2lkIGRlc2NyaXB0aW9uPVByZW1pdW0gYWNjZXNzIHRvIEJyYXZlIFNlYXJjaAowMDI1Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDFlY2lkIGlzc3VhbmNlX2ludGVydmFsPVAxTQowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTFiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0tUbTZKYVpzc0FOUEJ2IiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFKb29ZcUJTbTFtdHJOOW54VUJ6ckZwbCIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSDc1p+SfPzYa31kyis/j76jiOXm+MxWT0dH8+9LJfNYFwo=" - - stagingBraveTalkPremiumTimeLimited = "MDAyNGxvY2F0aW9uIHRhbGsuYnJhdmVzb2Z0d2FyZS5jb20KMDAyZmlkZW50aWZpZXIgYnJhdmUtdGFsay1wcmVtaXVtIHNrdSB0b2tlbiB2MQowMDFmY2lkIHNrdT1icmF2ZS10YWxrLXByZW1pdW0KMDAxM2NpZCBwcmljZT03LjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMzFjaWQgZGVzY3JpcHRpb249UHJlbWl1bSBhY2Nlc3MgdG8gQnJhdmUgVGFsawowMDI1Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDFlY2lkIGlzc3VhbmNlX2ludGVydmFsPVAxRAowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTFiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0tUbTRGdGNuaXVUQU9iIiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFKb29XVEJTbTFtdHJOOW5nM0NwRzRtNCIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSDtKYgKBLxJ6P0NQ4ZFox1dDVf6yFu4gRsefmiwy7ZN5Qo=" - stagingBraveFirewallVPNPremiumTimeLimited = "MDAyM2xvY2F0aW9uIHZwbi5icmF2ZXNvZnR3YXJlLmNvbQowMDM3aWRlbnRpZmllciBicmF2ZS1maXJld2FsbC12cG4tcHJlbWl1bSBza3UgdG9rZW4gdjEKMDAxZWNpZCBza3U9YnJhdmUtdnBuLXByZW1pdW0KMDAxM2NpZCBwcmljZT05Ljk5CjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMWVjaWQgZGVzY3JpcHRpb249QnJhdmUgVlBOCjAwMjVjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZAowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMWJjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfTGh2NE9NMWFBUHhmbFkiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMUwwVkVoQlNtMW10ck45bkdCNGtaa2ZoIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlID/JefMepasfiYgJmd7seLIrnCYTGHe3u9UHOcVD5ZslCg==" - stagingBraveFirewallVPNPremiumTimeLimitedV2 = "MDAyM2xvY2F0aW9uIHZwbi5icmF2ZXNvZnR3YXJlLmNvbQowMDIxaWRlbnRpZmllciBicmF2ZS12cG4tcHJlbWl1bQowMDFlY2lkIHNrdT1icmF2ZS12cG4tcHJlbWl1bQowMDEzY2lkIHByaWNlPTkuOTkKMDAxNWNpZCBjdXJyZW5jeT1VU0QKMDAyNmNpZCBkZXNjcmlwdGlvbj1icmF2ZS12cG4tcHJlbWl1bQowMDI4Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQtdjIKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDJiY2lkIGVhY2hfY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMUQKMDAxZmNpZCBpc3N1ZXJfdG9rZW5fYnVmZmVyPTMxCjAwMWZjaWQgaXNzdWVyX3Rva2VuX292ZXJsYXA9MgowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTFiY2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0xodjRPTTFhQVB4ZmxZIiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFMMFZFaEJTbTFtdHJOOW5HQjRrWmtmaCIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSDUdtr4vnEuKViKOGA3uHEdd8FcCuaMITzdFNm0FV6w6go=" - stagingBraveFirewallVPNPremiumTimeLimitedV2BAT = "MDAyM2xvY2F0aW9uIHZwbi5icmF2ZXNvZnR3YXJlLmNvbQowMDIxaWRlbnRpZmllciBicmF2ZS12cG4tcHJlbWl1bQowMDFlY2lkIHNrdT1icmF2ZS12cG4tcHJlbWl1bQowMDExY2lkIHByaWNlPTE1CjAwMTVjaWQgY3VycmVuY3k9QkFUCjAwMjZjaWQgZGVzY3JpcHRpb249YnJhdmUtdnBuLXByZW1pdW0KMDAyOGNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkLXYyCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyYmNpZCBlYWNoX2NyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFECjAwMWZjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zMQowMDFmY2lkIGlzc3Vlcl90b2tlbl9vdmVybGFwPTIKMDAyNmNpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1yYWRvbQowMGU0Y2lkIG1ldGFkYXRhPSB7ICJyYWRvbV9wcm9kdWN0X2lkIjogInByb2RfTGh2NE9NMWFBUHhmbFkiLCAicmFkb21fc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlc29mdHdhcmUuY29tL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAicmFkb21fY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmVzb2Z0d2FyZS5jb20vcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIL1gyoBprFu2lcbCvuRoMgPBfDZVFhJ3YYTZQdhWqDYnCg==" - - stagingBrave1MTimeLimitedV2 = "MDAzMGxvY2F0aW9uIHByZW1pdW1mcmVldHJpYWwuYnJhdmVzb2Z0d2FyZS5jb20KMDAyZmlkZW50aWZpZXIgYnJhdmUtZnJlZS0xbS10bHYyIHNrdSB0b2tlbiB2MQowMDFmY2lkIHNrdT1icmF2ZS1mcmVlLTFtLXRsdjIKMDAxMGNpZCBwcmljZT0wCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwNDBjaWQgZGVzY3JpcHRpb249RnJlZSB0cmlhbCBhY2Nlc3MgdG8gQnJhdmUgcHJlbWl1bSBwcm9kdWN0cwowMDI4Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQtdjIKMDAyOGNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVBUNjBTCjAwMWZjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zMAowMDFmY2lkIGlzc3Vlcl90b2tlbl9vdmVybGFwPTEKMDAyZnNpZ25hdHVyZSCCLkg37iCp1uKAYh7MiUQLjILHDWB7tQh1mMXFISCtYgo=" - stagingBrave5MTimeLimitedV2 = "MDAzMGxvY2F0aW9uIHByZW1pdW1mcmVldHJpYWwuYnJhdmVzb2Z0d2FyZS5jb20KMDAyZmlkZW50aWZpZXIgYnJhdmUtZnJlZS01bS10bHYyIHNrdSB0b2tlbiB2MQowMDFmY2lkIHNrdT1icmF2ZS1mcmVlLTVtLXRsdjIKMDAxMGNpZCBwcmljZT0wCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwNDBjaWQgZGVzY3JpcHRpb249RnJlZSB0cmlhbCBhY2Nlc3MgdG8gQnJhdmUgcHJlbWl1bSBwcm9kdWN0cwowMDI4Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQtdjIKMDAyOWNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVBUMzAwUwowMDFmY2lkIGlzc3Vlcl90b2tlbl9idWZmZXI9MzAKMDAxZmNpZCBpc3N1ZXJfdG9rZW5fb3ZlcmxhcD0xCjAwMmZzaWduYXR1cmUgBkRRgn1Y5SDmnwnsCfYl3JWpfb/OL5LrFqYezBlc3osK" - - devUserWalletVote = "AgEJYnJhdmUuY29tAiNicmF2ZSB1c2VyLXdhbGxldC12b3RlIHNrdSB0b2tlbiB2MQACFHNrdT11c2VyLXdhbGxldC12b3RlAAIKcHJpY2U9MC4yNQACDGN1cnJlbmN5PUJBVAACDGRlc2NyaXB0aW9uPQACGmNyZWRlbnRpYWxfdHlwZT1zaW5nbGUtdXNlAAAGINiB9dUmpqLyeSEdZ23E4dPXwIBOUNJCFN9d5toIME2M" - devAnonCardVote = "AgEJYnJhdmUuY29tAiFicmF2ZSBhbm9uLWNhcmQtdm90ZSBza3UgdG9rZW4gdjEAAhJza3U9YW5vbi1jYXJkLXZvdGUAAgpwcmljZT0wLjI1AAIMY3VycmVuY3k9QkFUAAIMZGVzY3JpcHRpb249AAIaY3JlZGVudGlhbF90eXBlPXNpbmdsZS11c2UAAAYgPpv+Al9jRgVCaR49/AoRrsjQqXGqkwaNfqVka00SJxQ=" - devSearchClosedBeta = "AgEVc2VhcmNoLmJyYXZlLnNvZnR3YXJlAh9zZWFyY2ggY2xvc2VkIGJldGEgcHJvZ3JhbSBkZW1vAAIWc2t1PXNlYXJjaC1iZXRhLWFjY2VzcwACB3ByaWNlPTAAAgxjdXJyZW5jeT1CQVQAAi1kZXNjcmlwdGlvbj1TZWFyY2ggY2xvc2VkIGJldGEgcHJvZ3JhbSBhY2Nlc3MAAhpjcmVkZW50aWFsX3R5cGU9c2luZ2xlLXVzZQAABiB3uXfAAkNSRQd24jSauRny3VM0BYZ8yOclPTEgPa0xrA==" - devFreeTimeLimitedV2 = "MDAzMWxvY2F0aW9uIGZyZWUudGltZS5saW1pdGVkLnYyLmJyYXZlLnNvZnR3YXJlCjAwMjhpZGVudGlmaWVyIGZyZWUtdGltZS1saW1pdGVkLXYyLWRldgowMDI1Y2lkIHNrdT1mcmVlLXRpbWUtbGltaXRlZC12Mi1kZXYKMDAxMGNpZCBwcmljZT0wCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMmRjaWQgZGVzY3JpcHRpb249ZnJlZS10aW1lLWxpbWl0ZWQtdjItZGV2CjAwMjhjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZC12MgowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMWZjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zMAowMDFmY2lkIGlzc3Vlcl90b2tlbl9vdmVybGFwPTEKMDAyN2NpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1zdHJpcGUKMDAyZnNpZ25hdHVyZSAqgung8GCnS0TDch62es768kupFxaEMD1yMSgJX2apdgo=" - - devBraveTalkPremiumTimeLimited = "MDAyMWxvY2F0aW9uIHRhbGsuYnJhdmUuc29mdHdhcmUKMDAyZmlkZW50aWZpZXIgYnJhdmUtdGFsay1wcmVtaXVtIHNrdSB0b2tlbiB2MQowMDFmY2lkIHNrdT1icmF2ZS10YWxrLXByZW1pdW0KMDAxM2NpZCBwcmljZT03LjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMzFjaWQgZGVzY3JpcHRpb249UHJlbWl1bSBhY2Nlc3MgdG8gQnJhdmUgVGFsawowMDI1Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTE1Y2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0psYzIyNGhGdkFNdkVwIiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFKODRvTUhvZjIwYnBoRzZOQkFUMnZvciIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSB2eBNwpQ6AtZIy3ZNB8cFB00Fj3pe0YEtEs7O7dkunjAo=" - devBraveSearchPremiumTimeLimited = "MDAyM2xvY2F0aW9uIHNlYXJjaC5icmF2ZS5zb2Z0d2FyZQowMDMxaWRlbnRpZmllciBicmF2ZS1zZWFyY2gtcHJlbWl1bSBza3UgdG9rZW4gdjEKMDAyMWNpZCBza3U9YnJhdmUtc2VhcmNoLXByZW1pdW0KMDAxM2NpZCBwcmljZT0zLjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMzNjaWQgZGVzY3JpcHRpb249UHJlbWl1bSBhY2Nlc3MgdG8gQnJhdmUgU2VhcmNoCjAwMjVjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZAowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMTVjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfSnpTZXZ5Wk01aUJTcmYiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMUpMVGpISG9mMjBicGhHNjBXWWNQY2drIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuc29mdHdhcmUvYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuc29mdHdhcmUvcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIAhy/5h5ssBPusHhT6UPev8JIeKkOJ7l012rVGkxlcDsCg==" - devBraveSearchPremiumTimeLimitedV2 = "MDAyM2xvY2F0aW9uIHNlYXJjaC5icmF2ZS5zb2Z0d2FyZQowMDMxaWRlbnRpZmllciBicmF2ZS1zZWFyY2gtcHJlbWl1bSBza3UgdG9rZW4gdjEKMDAyMWNpZCBza3U9YnJhdmUtc2VhcmNoLXByZW1pdW0KMDAxM2NpZCBwcmljZT0zLjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMzNjaWQgZGVzY3JpcHRpb249UHJlbWl1bSBhY2Nlc3MgdG8gQnJhdmUgU2VhcmNoCjAwMjVjaWQgY3JlZGVudGlhbF90eXBlPXRpbWUtbGltaXRlZAowMDI2Y2lkIGNyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFNCjAwMWVjaWQgaXNzdWFuY2VfaW50ZXJ2YWw9UDFNCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMTVjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfSnpTZXZ5Wk01aUJTcmYiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMUpMVGpISG9mMjBicGhHNjBXWWNQY2drIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuc29mdHdhcmUvYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuc29mdHdhcmUvcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIO/u4ackB8DxBhajNe+5E+encUhHE6A5Zq0JXXTQjLoWCg==" - - devBraveSearchPremiumYearTimeLimited = "MDAyM2xvY2F0aW9uIHNlYXJjaC5icmF2ZS5zb2Z0d2FyZQowMDM2aWRlbnRpZmllciBicmF2ZS1zZWFyY2gtcHJlbWl1bS15ZWFyIHNrdSB0b2tlbiB2MQowMDIwY2lkIHNrdT1icmF2ZS1zZWFyY2gtYWRmcmVlCjAwMTRjaWQgcHJpY2U9MzAuMDAKMDAxNWNpZCBjdXJyZW5jeT1VU0QKMDAzM2NpZCBkZXNjcmlwdGlvbj1QcmVtaXVtIGFjY2VzcyB0byBCcmF2ZSBTZWFyY2gKMDAyNWNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMVkKMDAxZWNpZCBpc3N1YW5jZV9pbnRlcnZhbD1QMU0KMDAyN2NpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1zdHJpcGUKMDExNWNpZCBtZXRhZGF0YT0geyAic3RyaXBlX3Byb2R1Y3RfaWQiOiAicHJvZF9KelNldnlaTTVpQlNyZiIsICJzdHJpcGVfaXRlbV9pZCI6ICJwcmljZV8xSm9YdkZIb2YyMGJwaEc2eUg2a1FpUEciLCAic3RyaXBlX3N1Y2Nlc3NfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInN0cmlwZV9jYW5jZWxfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9wbGFucy8/aW50ZW50PWNoZWNrb3V0IiB9CjAwMmZzaWduYXR1cmUgfSNU9u0uAbGm1Vi8dKoa9hcK71VeMzGUWq77io6sJgUK" - devBraveFirewallVPNPremiumTimeLimited = "MDAyMGxvY2F0aW9uIHZwbi5icmF2ZS5zb2Z0d2FyZQowMDM3aWRlbnRpZmllciBicmF2ZS1maXJld2FsbC12cG4tcHJlbWl1bSBza3UgdG9rZW4gdjEKMDAyN2NpZCBza3U9YnJhdmUtZmlyZXdhbGwtdnBuLXByZW1pdW0KMDAxM2NpZCBwcmljZT05Ljk5CjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMjljaWQgZGVzY3JpcHRpb249QnJhdmUgRmlyZXdhbGwgKyBWUE4KMDAyNWNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyN2NpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1zdHJpcGUKMDExNWNpZCBtZXRhZGF0YT0geyAic3RyaXBlX3Byb2R1Y3RfaWQiOiAicHJvZF9LMWM4VzNvTTRtVXNHdyIsICJzdHJpcGVfaXRlbV9pZCI6ICJwcmljZV8xSk5ZdU5Ib2YyMGJwaEc2QnZnZVlFbnQiLCAic3RyaXBlX3N1Y2Nlc3NfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInN0cmlwZV9jYW5jZWxfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9wbGFucy8/aW50ZW50PWNoZWNrb3V0IiB9CjAwMmZzaWduYXR1cmUgZoDg2iXb36IocwS9/MZnvP5Hk2NfAdJ6qMs0kBSyinUK" - devBraveFirewallVPNPremiumTimeLimitedV2 = "MDAyMGxvY2F0aW9uIHZwbi5icmF2ZS5zb2Z0d2FyZQowMDIxaWRlbnRpZmllciBicmF2ZS12cG4tcHJlbWl1bQowMDI3Y2lkIHNrdT1icmF2ZS1maXJld2FsbC12cG4tcHJlbWl1bQowMDEzY2lkIHByaWNlPTkuOTkKMDAxNWNpZCBjdXJyZW5jeT1VU0QKMDAyOWNpZCBkZXNjcmlwdGlvbj1CcmF2ZSBGaXJld2FsbCArIFZQTgowMDI4Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQtdjIKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxTQowMDJiY2lkIGVhY2hfY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMUQKMDAxZmNpZCBpc3N1ZXJfdG9rZW5fYnVmZmVyPTMxCjAwMWZjaWQgaXNzdWVyX3Rva2VuX292ZXJsYXA9MgowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTE1Y2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX0sxYzhXM29NNG1Vc0d3IiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFKTll1TkhvZjIwYnBoRzZCdmdlWUVudCIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSCjPGxUzapQKFcpaZiPizs30/xFDUkPTgCkfQN/cB9pnwo=" - devBraveFirewallVPNPremiumTimeLimitedV2BAT = "MDAyMGxvY2F0aW9uIHZwbi5icmF2ZS5zb2Z0d2FyZQowMDIxaWRlbnRpZmllciBicmF2ZS12cG4tcHJlbWl1bQowMDI3Y2lkIHNrdT1icmF2ZS1maXJld2FsbC12cG4tcHJlbWl1bQowMDExY2lkIHByaWNlPTE1CjAwMTVjaWQgY3VycmVuY3k9QkFUCjAwMjljaWQgZGVzY3JpcHRpb249QnJhdmUgRmlyZXdhbGwgKyBWUE4KMDAyOGNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkLXYyCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyYmNpZCBlYWNoX2NyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFECjAwMWZjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zMQowMDFmY2lkIGlzc3Vlcl90b2tlbl9vdmVybGFwPTIKMDAyNmNpZCBhbGxvd2VkX3BheW1lbnRfbWV0aG9kcz1yYWRvbQowMGQ2Y2lkIG1ldGFkYXRhPSB7ICJyYWRvbV9wcm9kdWN0X2lkIjogIm5vdCBkZWZpbmVkIiwgInJhZG9tX3N1Y2Nlc3NfdXJpIjogImh0dHBzOi8vYWNjb3VudC5icmF2ZS5zb2Z0d2FyZS9hY2NvdW50Lz9pbnRlbnQ9cHJvdmlzaW9uIiwgInJhZG9tX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSBdGmEv+zPzDso4iNwxXkovgNN+0EMdldX/6aCTMpGveQo=" - - devBraveLeoPremiumTimeLimitedV2 = "MDAyMGxvY2F0aW9uIGxlby5icmF2ZS5zb2Z0d2FyZQowMDIxaWRlbnRpZmllciBicmF2ZS1sZW8tcHJlbWl1bQowMDFlY2lkIHNrdT1icmF2ZS1sZW8tcHJlbWl1bQowMDE0Y2lkIHByaWNlPTE1LjAwCjAwMTVjaWQgY3VycmVuY3k9VVNECjAwMjZjaWQgZGVzY3JpcHRpb249YnJhdmUtbGVvLXByZW1pdW0KMDAyOGNpZCBjcmVkZW50aWFsX3R5cGU9dGltZS1saW1pdGVkLXYyCjAwMjZjaWQgY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMU0KMDAyYmNpZCBlYWNoX2NyZWRlbnRpYWxfdmFsaWRfZHVyYXRpb249UDFECjAwMWVjaWQgaXNzdWVyX3Rva2VuX2J1ZmZlcj0zCjAwMWZjaWQgaXNzdWVyX3Rva2VuX292ZXJsYXA9MAowMDI3Y2lkIGFsbG93ZWRfcGF5bWVudF9tZXRob2RzPXN0cmlwZQowMTE1Y2lkIG1ldGFkYXRhPSB7ICJzdHJpcGVfcHJvZHVjdF9pZCI6ICJwcm9kX090WkNYT0NJTzNBSkU2IiwgInN0cmlwZV9pdGVtX2lkIjogInByaWNlXzFPNW0zbEhvZjIwYnBoRzZEbG9BTkFjYyIsICJzdHJpcGVfc3VjY2Vzc191cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL2FjY291bnQvP2ludGVudD1wcm92aXNpb24iLCAic3RyaXBlX2NhbmNlbF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50LmJyYXZlLnNvZnR3YXJlL3BsYW5zLz9pbnRlbnQ9Y2hlY2tvdXQiIH0KMDAyZnNpZ25hdHVyZSD+Y3cVuULUWTgdqrq4d+plRmyaTG/pMmNpLTl1erBzxwo=" - - devBraveLeoYearlyPremiumTimeLimitedV2 = "MDAyMGxvY2F0aW9uIGxlby5icmF2ZS5zb2Z0d2FyZQowMDI2aWRlbnRpZmllciBicmF2ZS1sZW8tcHJlbWl1bS15ZWFyCjAwMjNjaWQgc2t1PWJyYXZlLWxlby1wcmVtaXVtLXllYXIKMDAxNWNpZCBwcmljZT0xMzUuMDAKMDAxNWNpZCBjdXJyZW5jeT1VU0QKMDAyNmNpZCBkZXNjcmlwdGlvbj1icmF2ZS1sZW8tcHJlbWl1bQowMDI4Y2lkIGNyZWRlbnRpYWxfdHlwZT10aW1lLWxpbWl0ZWQtdjIKMDAyNmNpZCBjcmVkZW50aWFsX3ZhbGlkX2R1cmF0aW9uPVAxWQowMDJiY2lkIGVhY2hfY3JlZGVudGlhbF92YWxpZF9kdXJhdGlvbj1QMUQKMDAxZWNpZCBpc3N1ZXJfdG9rZW5fYnVmZmVyPTMKMDAxZmNpZCBpc3N1ZXJfdG9rZW5fb3ZlcmxhcD0wCjAwMjdjaWQgYWxsb3dlZF9wYXltZW50X21ldGhvZHM9c3RyaXBlCjAxMTVjaWQgbWV0YWRhdGE9IHsgInN0cmlwZV9wcm9kdWN0X2lkIjogInByb2RfT3RaQ1hPQ0lPM0FKRTYiLCAic3RyaXBlX2l0ZW1faWQiOiAicHJpY2VfMU82cmU4SG9mMjBicGhHNnRxZE5FRUFwIiwgInN0cmlwZV9zdWNjZXNzX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuc29mdHdhcmUvYWNjb3VudC8/aW50ZW50PXByb3Zpc2lvbiIsICJzdHJpcGVfY2FuY2VsX3VyaSI6ICJodHRwczovL2FjY291bnQuYnJhdmUuc29mdHdhcmUvcGxhbnMvP2ludGVudD1jaGVja291dCIgfQowMDJmc2lnbmF0dXJlIJqPHPzXhI1n/pi0lhN2iYFN12qtfKCL0rmPhOK16jB+Cg==" -) - -var skuMap = map[string]map[string]bool{ - "production": { - prodUserWalletVote: true, - prodAnonCardVote: true, - prodBraveTogetherPaid: true, - prodBraveTalkPremiumTimeLimited: true, - prodBraveSearchYearPremiumTimeLimited: true, - prodBraveSearchPremiumTimeLimited: true, - prodBraveFirewallVPNPremiumTimeLimitedV2: true, - prodBraveFirewallVPNPremiumTimeLimitedV2BAT: true, - prodBraveLeoPremiumTimeLimitedV2: true, - prodBraveLeoYearlyPremiumTimeLimitedV2: true, - }, - "staging": { - stagingUserWalletVote: true, - stagingAnonCardVote: true, - stagingWebtestPJSKUDemo: true, - stagingBraveTalkPremiumTimeLimited: true, - stagingBraveSearchPremiumTimeLimited: true, - stagingBraveSearchYearPremiumTimeLimited: true, - stagingBraveFirewallVPNPremiumTimeLimited: true, - stagingBraveFirewallVPNPremiumTimeLimitedV2: true, - stagingBraveFirewallVPNPremiumTimeLimitedV2BAT: true, - stagingBrave1MTimeLimitedV2: true, - stagingBrave5MTimeLimitedV2: true, - stagingBraveLeoPremiumTimeLimitedV2: true, - stagingBraveLeoYearlyPremiumTimeLimitedV2: true, - }, - "development": { - devUserWalletVote: true, - devAnonCardVote: true, - devSearchClosedBeta: true, - devBraveTalkPremiumTimeLimited: true, - devBraveSearchPremiumTimeLimited: true, - devBraveFirewallVPNPremiumTimeLimited: true, - devBraveSearchPremiumTimeLimitedV2: true, - devBraveSearchPremiumYearTimeLimited: true, - devBraveFirewallVPNPremiumTimeLimitedV2: true, - devBraveFirewallVPNPremiumTimeLimitedV2BAT: true, - devFreeTimeLimitedV2: true, - devBraveLeoPremiumTimeLimitedV2: true, - devBraveLeoYearlyPremiumTimeLimitedV2: true, - }, -} - -// temporary, until we can validate macaroon signatures -func validateHardcodedSku(ctx context.Context, sku string) (bool, error) { - // check sku white list from environment - whitelistSKUs, ok := ctx.Value(appctx.WhitelistSKUsCTXKey).([]string) - if ok { - for _, whitelistSKU := range whitelistSKUs { - if sku == whitelistSKU { - return true, nil - } - } - } - - // check hardcoded based on environment (non whitelisted) - env, err := appctx.GetStringFromContext(ctx, appctx.EnvironmentCTXKey) - if err != nil { - return false, fmt.Errorf("failed to get environment: %w", err) - } - valid, ok := skuMap[env][sku] - return valid && ok, nil -} diff --git a/services/skus/skustest/skustest.go b/services/skus/skustest/skustest.go deleted file mode 100644 index 46c323d12..000000000 --- a/services/skus/skustest/skustest.go +++ /dev/null @@ -1,67 +0,0 @@ -// Package skustest provides utilities for testing skus. Do not import this into non-test code. -package skustest - -import ( - "context" - "os" - "strings" - "testing" - - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - "github.com/jmoiron/sqlx" - "github.com/segmentio/kafka-go" - "github.com/stretchr/testify/assert" -) - -var tables = []string{"vote_drain", "api_keys", "transactions", "signing_order_request_outbox", - "time_limited_v2_order_creds", "order_creds", "order_cred_issuers", "order_items", "orders"} - -// Migrate - perform a migration for skus -func Migrate(t *testing.T) { - postgres, err := datastore.NewPostgres("", false, "skus_db") - assert.NoError(t, err) - - migrate, err := postgres.NewMigrate() - assert.NoError(t, err) - - version, dirty, _ := migrate.Version() - if dirty { - assert.NoError(t, migrate.Force(int(version))) - } - - if version > 0 { - assert.NoError(t, migrate.Down()) - } - - err = postgres.Migrate() - assert.NoError(t, err) -} - -// CleanDB - clean up the test db fixtures -func CleanDB(t *testing.T, datastore *sqlx.DB) { - for _, table := range tables { - _, err := datastore.Exec("delete from " + table) - assert.NoError(t, err) - } -} - -// SetupKafka is a test helper to setup kafka brokers and topic -func SetupKafka(ctx context.Context, t *testing.T, topics ...string) context.Context { - kafkaBrokers := os.Getenv("KAFKA_BROKERS") - ctx = context.WithValue(ctx, appctx.KafkaBrokersCTXKey, kafkaBrokers) - - dialer, _, err := kafkautils.TLSDialer() - assert.NoError(t, err) - - for _, topic := range topics { - conn, err := dialer.DialLeader(ctx, "tcp", strings.Split(kafkaBrokers, ",")[0], topic, 0) - assert.NoError(t, err) - - err = conn.CreateTopics(kafka.TopicConfig{Topic: topic, NumPartitions: 1, ReplicationFactor: 1}) - assert.NoError(t, err) - } - - return ctx -} diff --git a/services/skus/storage/repository/issuer.go b/services/skus/storage/repository/issuer.go deleted file mode 100644 index 32981587d..000000000 --- a/services/skus/storage/repository/issuer.go +++ /dev/null @@ -1,60 +0,0 @@ -package repository - -import ( - "context" - "database/sql" - "errors" - - "github.com/jmoiron/sqlx" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -type Issuer struct{} - -func NewIssuer() *Issuer { return &Issuer{} } - -func (r *Issuer) GetByMerchID(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - const q = `SELECT id, created_at, merchant_id, public_key - FROM order_cred_issuers WHERE merchant_id = $1` - - result := &model.Issuer{} - if err := sqlx.GetContext(ctx, dbi, result, q, merchID); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrIssuerNotFound - } - - return nil, err - } - - return result, nil -} - -func (r *Issuer) GetByPubKey(ctx context.Context, dbi sqlx.QueryerContext, pubKey string) (*model.Issuer, error) { - const q = `SELECT id, created_at, merchant_id, public_key - FROM order_cred_issuers WHERE public_key = $1` - - result := &model.Issuer{} - if err := sqlx.GetContext(ctx, dbi, result, q, pubKey); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrIssuerNotFound - } - - return nil, err - } - - return result, nil -} - -func (r *Issuer) Create(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - const q = `INSERT INTO order_cred_issuers (merchant_id, public_key) - VALUES ($1, $2) - RETURNING id, created_at, merchant_id, public_key` - - result := &model.Issuer{} - if err := dbi.QueryRowxContext(ctx, q, req.MerchantID, req.PublicKey).StructScan(result); err != nil { - return nil, err - } - - return result, nil -} diff --git a/services/skus/storage/repository/issuer_test.go b/services/skus/storage/repository/issuer_test.go deleted file mode 100644 index 476d7d597..000000000 --- a/services/skus/storage/repository/issuer_test.go +++ /dev/null @@ -1,284 +0,0 @@ -//go:build integration - -package repository_test - -import ( - "context" - "database/sql" - "errors" - "testing" - - "github.com/jmoiron/sqlx" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -func TestIssuer_GetByMerchID(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - t.Cleanup(func() { - _, _ = dbi.Exec("TRUNCATE_TABLE order_cred_issuers;") - }) - - type tcGiven struct { - merchID string - mid string - pkey string - } - - type tcExpected struct { - result *model.Issuer - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "not_found", - given: tcGiven{ - merchID: "not_found", - }, - exp: tcExpected{ - err: model.ErrIssuerNotFound, - }, - }, - - { - name: "result_1", - given: tcGiven{ - merchID: "merch_id", - mid: "merch_id", - pkey: "public_key", - }, - exp: tcExpected{ - result: &model.Issuer{ - MerchantID: "merch_id", - PublicKey: "public_key", - }, - }, - }, - } - - repo := repository.NewIssuer() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - if tc.given.mid != "" { - err := seedIssuerForTest(ctx, tx, tc.given.mid, tc.given.pkey) - must.Equal(t, nil, err) - } - - actual, err := repo.GetByMerchID(ctx, tx, tc.given.merchID) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - should.Equal(t, tc.exp.result.MerchantID, actual.MerchantID) - should.Equal(t, tc.exp.result.PublicKey, actual.PublicKey) - }) - } -} - -func TestIssuer_GetByPubKey(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - t.Cleanup(func() { - _, _ = dbi.Exec("TRUNCATE_TABLE order_cred_issuers;") - }) - - type tcGiven struct { - pubKey string - mid string - pkey string - } - - type tcExpected struct { - result *model.Issuer - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "not_found", - given: tcGiven{ - pubKey: "not_found", - }, - exp: tcExpected{ - err: model.ErrIssuerNotFound, - }, - }, - - { - name: "result_1", - given: tcGiven{ - pubKey: "public_key", - mid: "merch_id", - pkey: "public_key", - }, - exp: tcExpected{ - result: &model.Issuer{ - MerchantID: "merch_id", - PublicKey: "public_key", - }, - }, - }, - } - - repo := repository.NewIssuer() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - if tc.given.pkey != "" { - err := seedIssuerForTest(ctx, tx, tc.given.mid, tc.given.pkey) - must.Equal(t, nil, err) - } - - actual, err := repo.GetByPubKey(ctx, tx, tc.given.pubKey) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - should.Equal(t, tc.exp.result.MerchantID, actual.MerchantID) - should.Equal(t, tc.exp.result.PublicKey, actual.PublicKey) - }) - } -} - -func TestIssuer_Create(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - t.Cleanup(func() { - _, _ = dbi.Exec("TRUNCATE_TABLE order_cred_issuers;") - }) - - type tcGiven struct { - req model.IssuerNew - } - - type tcExpected struct { - result *model.Issuer - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "result_1", - given: tcGiven{ - req: model.IssuerNew{ - MerchantID: "merch_id_1", - PublicKey: "public_key_1", - }, - }, - exp: tcExpected{ - result: &model.Issuer{ - MerchantID: "merch_id_1", - PublicKey: "public_key_1", - }, - }, - }, - - { - name: "result_2", - given: tcGiven{ - req: model.IssuerNew{ - MerchantID: "merch_id_2", - PublicKey: "public_key_2", - }, - }, - exp: tcExpected{ - result: &model.Issuer{ - MerchantID: "merch_id_2", - PublicKey: "public_key_2", - }, - }, - }, - } - - repo := repository.NewIssuer() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - actual1, err := repo.Create(ctx, tx, tc.given.req) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - should.Equal(t, tc.exp.result.MerchantID, actual1.MerchantID) - should.Equal(t, tc.exp.result.PublicKey, actual1.PublicKey) - - actual2, err := repo.GetByMerchID(ctx, tx, actual1.MerchantID) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - should.Equal(t, actual1, actual2) - - actual3, err := repo.GetByPubKey(ctx, tx, actual2.PublicKey) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - should.Equal(t, actual2, actual3) - should.Equal(t, actual1, actual3) - }) - } -} - -func seedIssuerForTest(ctx context.Context, dbi sqlx.ExecerContext, mid, pkey string) error { - const q = `INSERT INTO order_cred_issuers (merchant_id, public_key) - VALUES ($1, $2)` - - if _, err := dbi.ExecContext(ctx, q, mid, pkey); err != nil { - return err - } - - return nil -} diff --git a/services/skus/storage/repository/mock.go b/services/skus/storage/repository/mock.go deleted file mode 100644 index d12fdca58..000000000 --- a/services/skus/storage/repository/mock.go +++ /dev/null @@ -1,78 +0,0 @@ -package repository - -import ( - "context" - "time" - - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -type MockOrder struct { - FnGet func(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) -} - -func (r *MockOrder) Get(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - if r.FnGet == nil { - result := &model.Order{ - ID: uuid.NewV4(), - } - - return result, nil - } - - return r.FnGet(ctx, dbi, id) -} - -type MockIssuer struct { - FnGetByMerchID func(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) - FnGetByPubKey func(ctx context.Context, dbi sqlx.QueryerContext, pubKey string) (*model.Issuer, error) - FnCreate func(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) -} - -func (r *MockIssuer) GetByMerchID(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { - if r.FnGetByMerchID == nil { - result := &model.Issuer{ - ID: uuid.NewV4(), - MerchantID: merchID, - PublicKey: "public_key", - CreatedAt: time.Now().UTC(), - } - - return result, nil - } - - return r.FnGetByMerchID(ctx, dbi, merchID) -} - -func (r *MockIssuer) GetByPubKey(ctx context.Context, dbi sqlx.QueryerContext, pubKey string) (*model.Issuer, error) { - if r.FnGetByPubKey == nil { - result := &model.Issuer{ - ID: uuid.NewV4(), - MerchantID: "merchant_id", - PublicKey: pubKey, - CreatedAt: time.Now().UTC(), - } - - return result, nil - } - - return r.FnGetByPubKey(ctx, dbi, pubKey) -} - -func (r *MockIssuer) Create(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { - if r.FnCreate == nil { - result := &model.Issuer{ - ID: uuid.NewV4(), - MerchantID: req.MerchantID, - PublicKey: req.PublicKey, - CreatedAt: time.Now().UTC(), - } - - return result, nil - } - - return r.FnCreate(ctx, dbi, req) -} diff --git a/services/skus/storage/repository/order_history.go b/services/skus/storage/repository/order_history.go deleted file mode 100644 index 63dd3eba8..000000000 --- a/services/skus/storage/repository/order_history.go +++ /dev/null @@ -1,35 +0,0 @@ -package repository - -import ( - "context" - "time" - - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -type OrderPayHistory struct{} - -func NewOrderPayHistory() *OrderPayHistory { return &OrderPayHistory{} } - -func (r *OrderPayHistory) Insert(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error { - const q = `INSERT INTO order_payment_history (order_id, last_paid) VALUES ($1, $2)` - - result, err := dbi.ExecContext(ctx, q, id, when) - if err != nil { - return err - } - - numAffected, err := result.RowsAffected() - if err != nil { - return err - } - - if numAffected == 0 { - return model.ErrNoRowsChangedOrderPayHistory - } - - return nil -} diff --git a/services/skus/storage/repository/order_item.go b/services/skus/storage/repository/order_item.go deleted file mode 100644 index d6a706ffb..000000000 --- a/services/skus/storage/repository/order_item.go +++ /dev/null @@ -1,81 +0,0 @@ -package repository - -import ( - "context" - "database/sql" - "errors" - - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -type OrderItem struct{} - -func NewOrderItem() *OrderItem { return &OrderItem{} } - -// Get retrieves the order item by the given id. -func (r *OrderItem) Get(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.OrderItem, error) { - const q = ` - SELECT - id, order_id, sku, created_at, updated_at, currency, - quantity, price, (quantity * price) as subtotal, - location, description, credential_type,metadata, valid_for_iso, issuance_interval - FROM order_items WHERE id = $1` - - result := &model.OrderItem{} - if err := sqlx.GetContext(ctx, dbi, result, q, id); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrOrderItemNotFound - } - - return nil, err - } - - return result, nil -} - -// FindByOrderID returns order items for the given orderID. -func (r *OrderItem) FindByOrderID(ctx context.Context, dbi sqlx.QueryerContext, orderID uuid.UUID) ([]model.OrderItem, error) { - const q = ` - SELECT - id, order_id, sku, created_at, updated_at, currency, - quantity, price, (quantity * price) as subtotal, - location, description, credential_type, metadata, valid_for_iso, issuance_interval - FROM order_items WHERE order_id = $1` - - result := make([]model.OrderItem, 0) - if err := sqlx.SelectContext(ctx, dbi, &result, q, orderID); err != nil { - return nil, err - } - - return result, nil -} - -// InsertMany inserts given items and returns the result. -func (r *OrderItem) InsertMany(ctx context.Context, dbi sqlx.ExtContext, items ...model.OrderItem) ([]model.OrderItem, error) { - if len(items) == 0 { - return []model.OrderItem{}, nil - } - - const q = ` - INSERT INTO order_items ( - order_id, sku, quantity, price, currency, subtotal, location, description, credential_type, metadata, valid_for, valid_for_iso, issuance_interval - ) VALUES ( - :order_id, :sku, :quantity, :price, :currency, :subtotal, :location, :description, :credential_type, :metadata, :valid_for, :valid_for_iso, :issuance_interval - ) RETURNING id, order_id, sku, created_at, updated_at, currency, quantity, price, location, description, credential_type, (quantity * price) as subtotal, metadata, valid_for` - - rows, err := sqlx.NamedQueryContext(ctx, dbi, q, items) - if err != nil { - return nil, err - } - defer func() { _ = rows.Close() }() - - result := make([]model.OrderItem, 0, len(items)) - if err := sqlx.StructScan(rows, &result); err != nil { - return nil, err - } - - return result, nil -} diff --git a/services/skus/storage/repository/order_item_test.go b/services/skus/storage/repository/order_item_test.go deleted file mode 100644 index 4b1427aff..000000000 --- a/services/skus/storage/repository/order_item_test.go +++ /dev/null @@ -1,223 +0,0 @@ -//go:build integration - -package repository_test - -import ( - "context" - "database/sql" - "testing" - - "github.com/jmoiron/sqlx" - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/libs/datastore" - - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -func TestOrderItem_InsertMany(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - defer func() { - _, _ = dbi.Exec("TRUNCATE_TABLE order_items, orders;") - }() - - type testCase struct { - name string - given []model.OrderItem - exp []model.OrderItem - } - - tests := []testCase{ - { - name: "empty_input", - exp: []model.OrderItem{}, - }, - - { - name: "one_item", - given: []model.OrderItem{ - { - SKU: "sku_01_01", - Quantity: 1, - Price: mustDecimalFromString("2"), - Currency: "USD", - Subtotal: mustDecimalFromString("2"), - CredentialType: "something", - }, - }, - - exp: []model.OrderItem{ - { - SKU: "sku_01_01", - Quantity: 1, - Price: mustDecimalFromString("2"), - Currency: "USD", - Subtotal: mustDecimalFromString("2"), - CredentialType: "something", - }, - }, - }, - - { - name: "two_items", - given: []model.OrderItem{ - { - SKU: "sku_02_01", - Quantity: 2, - Price: mustDecimalFromString("3"), - Currency: "USD", - Subtotal: mustDecimalFromString("6"), - CredentialType: "something", - }, - - { - SKU: "sku_02_02", - Quantity: 3, - Price: mustDecimalFromString("4"), - Currency: "USD", - Subtotal: mustDecimalFromString("12"), - CredentialType: "something", - }, - }, - - exp: []model.OrderItem{ - { - SKU: "sku_02_01", - Quantity: 2, - Price: mustDecimalFromString("3"), - Currency: "USD", - Subtotal: mustDecimalFromString("6"), - CredentialType: "something", - }, - - { - SKU: "sku_02_02", - Quantity: 3, - Price: mustDecimalFromString("4"), - Currency: "USD", - Subtotal: mustDecimalFromString("12"), - CredentialType: "something", - }, - }, - }, - } - - orepo := repository.NewOrder() - iorepo := repository.NewOrderItem() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - order, err := createOrderForTest(ctx, tx, orepo) - must.Equal(t, nil, err) - - model.OrderItemList(tc.given).SetOrderID(order.ID) - - actual, err := iorepo.InsertMany(ctx, tx, tc.given...) - must.Equal(t, nil, err) - - must.Equal(t, len(tc.exp), len(actual)) - - // Check each item manually as ids are generated. - for j := range tc.exp { - should.NotEqual(t, uuid.Nil, actual[j].ID) - should.Equal(t, order.ID, actual[j].OrderID) - should.Equal(t, tc.exp[j].SKU, actual[j].SKU) - should.Equal(t, tc.exp[j].Quantity, actual[j].Quantity) - should.Equal(t, tc.exp[j].Price.String(), actual[j].Price.String()) - should.Equal(t, tc.exp[j].Currency, actual[j].Currency) - should.Equal(t, tc.exp[j].Subtotal.String(), actual[j].Subtotal.String()) - should.Equal(t, tc.exp[j].CredentialType, actual[j].CredentialType) - } - }) - } -} - -func setupDBI() (*sqlx.DB, error) { - pg, err := datastore.NewPostgres("", false, "") - if err != nil { - return nil, err - } - - mg, err := pg.NewMigrate() - if err != nil { - return nil, err - } - - ver, dirty, err := mg.Version() - if err != nil { - return nil, err - } - - if dirty { - if err := mg.Force(int(ver)); err != nil { - return nil, err - } - } - - if ver > 0 { - if err := mg.Down(); err != nil { - return nil, err - } - } - - if err := pg.Migrate(); err != nil { - return nil, err - } - - return pg.RawDB(), nil -} - -type orderCreator interface { - Create(ctx context.Context, dbi sqlx.QueryerContext, req *model.OrderNew) (*model.Order, error) -} - -func createOrderForTest(ctx context.Context, dbi sqlx.QueryerContext, repo orderCreator) (*model.Order, error) { - price, err := decimal.NewFromString("187") - if err != nil { - return nil, err - } - - req := &model.OrderNew{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: sql.NullString{ - Valid: true, - String: "somelocation", - }, - TotalPrice: price, - AllowedPaymentMethods: pq.StringArray{"stripe"}, - } - - result, err := repo.Create(ctx, dbi, req) - if err != nil { - return nil, err - } - - return result, nil -} - -func mustDecimalFromString(v string) decimal.Decimal { - result, err := decimal.NewFromString(v) - if err != nil { - panic(err) - } - - return result -} diff --git a/services/skus/storage/repository/repository.go b/services/skus/storage/repository/repository.go deleted file mode 100644 index f4132d12b..000000000 --- a/services/skus/storage/repository/repository.go +++ /dev/null @@ -1,274 +0,0 @@ -// Package repository provides access to data available in SQL-based data store. -package repository - -import ( - "context" - "database/sql" - "errors" - "time" - - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - - "github.com/brave-intl/bat-go/libs/datastore" - - "github.com/brave-intl/bat-go/services/skus/model" -) - -type Order struct{} - -func NewOrder() *Order { return &Order{} } - -// Get retrieves the order for the given id. -func (r *Order) Get(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (*model.Order, error) { - const q = `SELECT - id, created_at, currency, updated_at, total_price, - merchant_id, location, status, allowed_payment_methods, - metadata, valid_for, last_paid_at, expires_at, trial_days - FROM orders WHERE id = $1` - - result := &model.Order{} - if err := sqlx.GetContext(ctx, dbi, result, q, id); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrOrderNotFound - } - - return nil, err - } - - return result, nil -} - -// GetByExternalID retrieves the order by extID in metadata.externalID. -func (r *Order) GetByExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (*model.Order, error) { - const q = `SELECT - id, created_at, currency, updated_at, total_price, - merchant_id, location, status, allowed_payment_methods, - metadata, valid_for, last_paid_at, expires_at, trial_days - FROM orders WHERE metadata->>'externalID' = $1` - - result := &model.Order{} - if err := sqlx.GetContext(ctx, dbi, result, q, extID); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrOrderNotFound - } - - return nil, err - } - - return result, nil -} - -// Create creates an order with the data in req. -func (r *Order) Create(ctx context.Context, dbi sqlx.QueryerContext, oreq *model.OrderNew) (*model.Order, error) { - const q = `INSERT INTO orders - (total_price, merchant_id, status, currency, location, allowed_payment_methods, valid_for) - VALUES ($1, $2, $3, $4, $5, $6, $7) - RETURNING id, created_at, currency, updated_at, total_price, merchant_id, location, status, allowed_payment_methods, valid_for` - - result := &model.Order{} - if err := dbi.QueryRowxContext( - ctx, - q, - oreq.TotalPrice, - oreq.MerchantID, - oreq.Status, - oreq.Currency, - oreq.Location, - oreq.AllowedPaymentMethods, - oreq.ValidFor, - ).StructScan(result); err != nil { - return nil, err - } - - return result, nil -} - -// SetLastPaidAt sets last_paid_at to when. -func (r *Order) SetLastPaidAt(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error { - const q = `UPDATE orders SET last_paid_at = $2 WHERE id = $1` - - return r.execUpdate(ctx, dbi, q, id, when) -} - -// SetTrialDays sets trial_days to ndays. -func (r *Order) SetTrialDays(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID, ndays int64) (*model.Order, error) { - const q = `UPDATE orders - SET trial_days = $2, updated_at = now() - WHERE id = $1 - RETURNING id, created_at, currency, updated_at, total_price, merchant_id, location, status, allowed_payment_methods, metadata, valid_for, last_paid_at, expires_at, trial_days` - - result := &model.Order{} - if err := dbi.QueryRowxContext(ctx, q, id, ndays).StructScan(result); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrOrderNotFound - } - - return nil, err - } - - return result, nil -} - -// SetStatus sets status to status. -func (r *Order) SetStatus(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, status string) error { - const q = `UPDATE orders SET status = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1` - - return r.execUpdate(ctx, dbi, q, id, status) -} - -// GetTimeBounds returns valid_for and last_paid_at for the order. -func (r *Order) GetTimeBounds(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (model.OrderTimeBounds, error) { - const q = `SELECT valid_for, last_paid_at FROM orders WHERE id = $1` - - var result model.OrderTimeBounds - if err := sqlx.GetContext(ctx, dbi, &result, q, id); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return model.EmptyOrderTimeBounds(), model.ErrOrderNotFound - } - - return model.EmptyOrderTimeBounds(), err - } - - return result, nil -} - -// GetExpiresAtAfterISOPeriod returns a new value for expires_at that is last_paid_at plus ISO period. -// -// It falls back to now() when last_paid_at is NULL. -// It uses the maximum of the order items' valid_for_iso as inverval, and falls back to 1 month. -func (r *Order) GetExpiresAtAfterISOPeriod(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (time.Time, error) { - const q = `SELECT COALESCE(last_paid_at, now()) + - (SELECT COALESCE(MAX(valid_for_iso::interval), interval '1 month') FROM order_items WHERE order_id = $2) - AS expires_at - FROM orders WHERE id = $1` - - var result time.Time - if err := sqlx.GetContext(ctx, dbi, &result, q, id, id); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return time.Time{}, model.ErrOrderNotFound - } - - return time.Time{}, err - } - - return result, nil -} - -// SetExpiresAt sets expires_at. -func (r *Order) SetExpiresAt(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error { - const q = `UPDATE orders SET updated_at = CURRENT_TIMESTAMP, expires_at = $2 WHERE id = $1` - - return r.execUpdate(ctx, dbi, q, id, when) -} - -// UpdateMetadata _sets_ metadata to data. -func (r *Order) UpdateMetadata(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, data datastore.Metadata) error { - const q = `UPDATE orders SET metadata = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1` - - return r.execUpdate(ctx, dbi, q, id, data) -} - -// AppendMetadata sets value by key to order's metadata, and might create metadata if it was missing. -func (r *Order) AppendMetadata(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, key, val string) error { - const q = `UPDATE orders - SET metadata = COALESCE(metadata||jsonb_build_object($2::text, $3::text), metadata, jsonb_build_object($2::text, $3::text)), - updated_at = CURRENT_TIMESTAMP WHERE id = $1` - - return r.execUpdate(ctx, dbi, q, id, key, val) -} - -// AppendMetadataInt sets int value by key to order's metadata, and might create metadata if it was missing. -func (r *Order) AppendMetadataInt(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, key string, val int) error { - const q = `UPDATE orders - SET metadata = COALESCE(metadata||jsonb_build_object($2::text, $3::integer), metadata, jsonb_build_object($2::text, $3::integer)), - updated_at = CURRENT_TIMESTAMP where id = $1` - - return r.execUpdate(ctx, dbi, q, id, key, val) -} - -// AppendMetadataInt64 sets int value by key to order's metadata, and might create metadata if it was missing. -func (r *Order) AppendMetadataInt64(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, key string, val int64) error { - const q = `UPDATE orders - SET metadata = COALESCE(metadata||jsonb_build_object($2::text, $3::integer), metadata, jsonb_build_object($2::text, $3::integer)), - updated_at = CURRENT_TIMESTAMP where id = $1` - - return r.execUpdate(ctx, dbi, q, id, key, val) -} - -// GetExpiredStripeCheckoutSessionID returns stripeCheckoutSessionId if it's found and expired. -func (r *Order) GetExpiredStripeCheckoutSessionID(ctx context.Context, dbi sqlx.QueryerContext, orderID uuid.UUID) (string, error) { - const q = `SELECT metadata->>'stripeCheckoutSessionId' AS checkout_session - FROM orders - WHERE id = $1 AND metadata IS NOT NULL AND status='pending' AND updated_at>'externalID' = $1 AND metadata IS NOT NULL` - - var result bool - if err := sqlx.GetContext(ctx, dbi, &result, q, extID); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return false, nil - } - - return false, err - } - - return result, nil -} - -// GetMetadata returns metadata of the order. -func (r *Order) GetMetadata(ctx context.Context, dbi sqlx.QueryerContext, id uuid.UUID) (datastore.Metadata, error) { - const q = `SELECT metadata - FROM orders - WHERE id = $1 AND metadata IS NOT NULL` - - result := datastore.Metadata{} - if err := sqlx.GetContext(ctx, dbi, &result, q, id); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrOrderNotFound - } - - return nil, err - } - - return result, nil -} - -func (r *Order) execUpdate(ctx context.Context, dbi sqlx.ExecerContext, q string, args ...interface{}) error { - result, err := dbi.ExecContext(ctx, q, args...) - if err != nil { - return err - } - - numAffected, err := result.RowsAffected() - if err != nil { - return err - } - - if numAffected == 0 { - return model.ErrNoRowsChangedOrder - } - - return nil -} diff --git a/services/skus/storage/repository/repository_test.go b/services/skus/storage/repository/repository_test.go deleted file mode 100644 index 6280032bb..000000000 --- a/services/skus/storage/repository/repository_test.go +++ /dev/null @@ -1,981 +0,0 @@ -//go:build integration - -package repository_test - -import ( - "context" - "database/sql" - "errors" - "testing" - "time" - - "github.com/jmoiron/sqlx" - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - - "github.com/brave-intl/bat-go/libs/datastore" - timeutils "github.com/brave-intl/bat-go/libs/time" - - "github.com/brave-intl/bat-go/services/skus/model" - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -func TestOrder_SetTrialDays(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - t.Cleanup(func() { - _, _ = dbi.Exec("TRUNCATE_TABLE orders;") - }) - - type tcExpected struct { - ndays int64 - err error - } - - type testCase struct { - name string - given int64 - exp tcExpected - } - - tests := []testCase{ - { - name: "not_found", - exp: tcExpected{ - err: model.ErrOrderNotFound, - }, - }, - - { - name: "no_changes", - }, - - { - name: "updated_value", - given: 4, - exp: tcExpected{ndays: 4}, - }, - } - - repo := repository.NewOrder() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - order, err := createOrderForTest(ctx, tx, repo) - must.Equal(t, nil, err) - - id := order.ID - if tc.exp.err == model.ErrOrderNotFound { - // Use any id for testing the not found case. - id = uuid.NamespaceDNS - } - - actual, err := repo.SetTrialDays(ctx, tx, id, tc.given) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - should.Equal(t, tc.exp.ndays, actual.GetTrialDays()) - }) - } -} - -func TestOrder_AppendMetadata(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - defer func() { - _, _ = dbi.Exec("TRUNCATE_TABLE orders;") - }() - - type tcGiven struct { - data datastore.Metadata - key string - val string - } - - type tcExpected struct { - data datastore.Metadata - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "not_found", - exp: tcExpected{ - err: model.ErrNoRowsChangedOrder, - }, - }, - - { - name: "no_previous_metadata", - given: tcGiven{ - key: "key_01_01", - val: "value_01_01", - }, - exp: tcExpected{ - data: datastore.Metadata{"key_01_01": "value_01_01"}, - }, - }, - - { - name: "no_changes", - given: tcGiven{ - data: datastore.Metadata{"key_02_01": "value_02_01"}, - key: "key_02_01", - val: "value_02_01", - }, - exp: tcExpected{ - data: datastore.Metadata{"key_02_01": "value_02_01"}, - }, - }, - - { - name: "updates_the_only_key", - given: tcGiven{ - data: datastore.Metadata{"key_03_01": "value_03_01"}, - key: "key_03_01", - val: "value_03_01_UPDATED", - }, - exp: tcExpected{ - data: datastore.Metadata{"key_03_01": "value_03_01_UPDATED"}, - }, - }, - - { - name: "updates_one_from_many", - given: tcGiven{ - data: datastore.Metadata{ - "key_04_01": "value_04_01", - "key_04_02": "value_04_02", - "key_04_03": "value_04_03", - }, - key: "key_04_02", - val: "value_04_02_UPDATED", - }, - exp: tcExpected{ - data: datastore.Metadata{ - "key_04_01": "value_04_01", - "key_04_02": "value_04_02_UPDATED", - "key_04_03": "value_04_03", - }, - }, - }, - - { - name: "stripeSubscriptionId_add_with_no_previous_value", - given: tcGiven{ - data: datastore.Metadata{"key_02_01": "value_02_01"}, - key: "stripeSubscriptionId", - val: "9570bf21-98e8-4ddc-950d-a50121d48a0a", - }, - exp: tcExpected{ - data: datastore.Metadata{ - "key_02_01": "value_02_01", - "stripeSubscriptionId": "9570bf21-98e8-4ddc-950d-a50121d48a0a", - }, - }, - }, - - { - name: "stripeSubscriptionId_no_change", - given: tcGiven{ - data: datastore.Metadata{ - "key_02_01": "value_02_01", - "stripeSubscriptionId": "9570bf21-98e8-4ddc-950d-a50121d48a0a", - }, - key: "stripeSubscriptionId", - val: "9570bf21-98e8-4ddc-950d-a50121d48a0a", - }, - exp: tcExpected{ - data: datastore.Metadata{ - "key_02_01": "value_02_01", - "stripeSubscriptionId": "9570bf21-98e8-4ddc-950d-a50121d48a0a", - }, - }, - }, - - { - name: "stripeSubscriptionId_replace", - given: tcGiven{ - data: datastore.Metadata{ - "key_02_01": "value_02_01", - "stripeSubscriptionId": "9570bf21-98e8-4ddc-950d-a50121d48a0a", - }, - key: "stripeSubscriptionId", - val: "edfe50f8-06dc-4d5f-a6ca-15c8a1ce6afb", - }, - exp: tcExpected{ - data: datastore.Metadata{ - "key_02_01": "value_02_01", - "stripeSubscriptionId": "edfe50f8-06dc-4d5f-a6ca-15c8a1ce6afb", - }, - }, - }, - } - - repo := repository.NewOrder() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - order, err := createOrderForTest(ctx, tx, repo) - must.Equal(t, nil, err) - - id := order.ID - if tc.exp.err == model.ErrNoRowsChangedOrder { - // Use any id for testing the not found case. - id = uuid.NamespaceDNS - } - - if tc.given.data != nil { - err := repo.UpdateMetadata(ctx, tx, id, tc.given.data) - must.Equal(t, nil, err) - } - - { - err := repo.AppendMetadata(ctx, tx, id, tc.given.key, tc.given.val) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - } - - if tc.exp.err != nil { - return - } - - actual, err := repo.Get(ctx, tx, id) - must.Equal(t, nil, err) - - should.Equal(t, tc.exp.data, actual.Metadata) - }) - } -} - -func TestOrder_AppendMetadataInt(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - defer func() { - _, _ = dbi.Exec("TRUNCATE_TABLE orders;") - }() - - type tcGiven struct { - data datastore.Metadata - key string - val int - } - - type tcExpected struct { - data datastore.Metadata - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "not_found", - exp: tcExpected{ - err: model.ErrNoRowsChangedOrder, - }, - }, - - { - name: "no_previous_metadata", - given: tcGiven{ - key: "key_01_01", - val: 101, - }, - exp: tcExpected{ - data: datastore.Metadata{"key_01_01": float64(101)}, - }, - }, - - { - name: "no_changes", - given: tcGiven{ - data: datastore.Metadata{"key_02_01": 201}, - key: "key_02_01", - val: 201, - }, - exp: tcExpected{ - data: datastore.Metadata{"key_02_01": float64(201)}, - }, - }, - - { - name: "updates_the_only_key", - given: tcGiven{ - data: datastore.Metadata{"key_03_01": float64(301)}, - key: "key_03_01", - val: 30101, - }, - exp: tcExpected{ - data: datastore.Metadata{"key_03_01": float64(30101)}, - }, - }, - - { - name: "updates_one_from_many", - given: tcGiven{ - data: datastore.Metadata{ - "key_04_01": "key_04_01", - "key_04_02": float64(402), - "key_04_03": float64(403), - }, - key: "key_04_02", - val: 40201, - }, - exp: tcExpected{ - data: datastore.Metadata{ - "key_04_01": "key_04_01", - "key_04_02": float64(40201), - "key_04_03": float64(403), - }, - }, - }, - } - - repo := repository.NewOrder() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - order, err := createOrderForTest(ctx, tx, repo) - must.Equal(t, nil, err) - - id := order.ID - if tc.exp.err == model.ErrNoRowsChangedOrder { - // Use any id for testing the not found case. - id = uuid.NamespaceDNS - } - - if tc.given.data != nil { - err := repo.UpdateMetadata(ctx, tx, id, tc.given.data) - must.Equal(t, nil, err) - } - - { - err := repo.AppendMetadataInt(ctx, tx, id, tc.given.key, tc.given.val) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - } - - if tc.exp.err != nil { - return - } - - actual, err := repo.Get(ctx, tx, id) - must.Equal(t, nil, err) - - // This is currently failing. - // The expectation is that data fetched from the store would be int. - // It, however, is float64. - // - // Temporary defining expectations as float64 so that tests pass. - should.Equal(t, tc.exp.data, actual.Metadata) - }) - } -} - -func TestOrder_AppendMetadataInt64(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - defer func() { - _, _ = dbi.Exec("TRUNCATE_TABLE orders;") - }() - - type tcGiven struct { - data datastore.Metadata - key string - val int64 - } - - type tcExpected struct { - data datastore.Metadata - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "not_found", - exp: tcExpected{ - err: model.ErrNoRowsChangedOrder, - }, - }, - - { - name: "no_previous_metadata", - given: tcGiven{ - key: "key_01_01", - val: 101, - }, - exp: tcExpected{ - data: datastore.Metadata{"key_01_01": float64(101)}, - }, - }, - - { - name: "no_changes", - given: tcGiven{ - data: datastore.Metadata{"key_02_01": 201}, - key: "key_02_01", - val: 201, - }, - exp: tcExpected{ - data: datastore.Metadata{"key_02_01": float64(201)}, - }, - }, - - { - name: "updates_the_only_key", - given: tcGiven{ - data: datastore.Metadata{"key_03_01": float64(301)}, - key: "key_03_01", - val: 30101, - }, - exp: tcExpected{ - data: datastore.Metadata{"key_03_01": float64(30101)}, - }, - }, - - { - name: "updates_one_from_many", - given: tcGiven{ - data: datastore.Metadata{ - "key_04_01": "key_04_01", - "key_04_02": float64(402), - "key_04_03": float64(403), - }, - key: "key_04_02", - val: 40201, - }, - exp: tcExpected{ - data: datastore.Metadata{ - "key_04_01": "key_04_01", - "key_04_02": float64(40201), - "key_04_03": float64(403), - }, - }, - }, - } - - repo := repository.NewOrder() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - order, err := createOrderForTest(ctx, tx, repo) - must.Equal(t, nil, err) - - id := order.ID - if tc.exp.err == model.ErrNoRowsChangedOrder { - // Use any id for testing the not found case. - id = uuid.NamespaceDNS - } - - if tc.given.data != nil { - err := repo.UpdateMetadata(ctx, tx, id, tc.given.data) - must.Equal(t, nil, err) - } - - { - err := repo.AppendMetadataInt64(ctx, tx, id, tc.given.key, tc.given.val) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - } - - if tc.exp.err != nil { - return - } - - actual, err := repo.Get(ctx, tx, id) - must.Equal(t, nil, err) - - // This is currently failing. - // The expectation is that data fetched from the store would be int. - // It, however, is float64. - // - // Temporary defining expectations as float64 so that tests pass. - should.Equal(t, tc.exp.data, actual.Metadata) - }) - } -} - -func TestOrder_GetExpiresAtAfterISOPeriod(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - defer func() { - _, _ = dbi.Exec("TRUNCATE_TABLE orders;") - }() - - type tcGiven struct { - lastPaidAt time.Time - items []model.OrderItem - } - - type tcExpected struct { - expiresAt time.Time - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "no_last_paid_no_items", - }, - - { - name: "20230202_no_items", - given: tcGiven{ - lastPaidAt: time.Date(2023, time.February, 2, 1, 0, 0, 0, time.UTC), - }, - exp: tcExpected{ - expiresAt: time.Date(2023, time.March, 2, 1, 0, 0, 0, time.UTC), - }, - }, - - { - name: "20230202_1_item", - given: tcGiven{ - lastPaidAt: time.Date(2023, time.February, 2, 1, 0, 0, 0, time.UTC), - items: []model.OrderItem{ - { - SKU: "sku_01_01", - Quantity: 1, - Price: mustDecimalFromString("2"), - Currency: "USD", - Subtotal: mustDecimalFromString("2"), - CredentialType: "something", - ValidForISO: ptrString("P1M"), - }, - }, - }, - exp: tcExpected{ - expiresAt: time.Date(2023, time.March, 2, 1, 0, 0, 0, time.UTC), - }, - }, - - { - name: "20230331_2_items", - given: tcGiven{ - lastPaidAt: time.Date(2023, time.March, 31, 1, 0, 0, 0, time.UTC), - items: []model.OrderItem{ - { - SKU: "sku_02_01", - Quantity: 2, - Price: mustDecimalFromString("3"), - Currency: "USD", - Subtotal: mustDecimalFromString("6"), - CredentialType: "something", - ValidForISO: ptrString("P1M"), - }, - - { - SKU: "sku_02_02", - Quantity: 3, - Price: mustDecimalFromString("4"), - Currency: "USD", - Subtotal: mustDecimalFromString("12"), - CredentialType: "something", - ValidForISO: ptrString("P2M"), - }, - }, - }, - exp: tcExpected{ - expiresAt: time.Date(2023, time.May, 31, 1, 0, 0, 0, time.UTC), - }, - }, - - { - name: "20230331_2_items_no_iso", - given: tcGiven{ - lastPaidAt: time.Date(2023, time.March, 31, 1, 0, 0, 0, time.UTC), - items: []model.OrderItem{ - { - SKU: "sku_02_01", - Quantity: 2, - Price: mustDecimalFromString("3"), - Currency: "USD", - Subtotal: mustDecimalFromString("6"), - CredentialType: "something", - }, - - { - SKU: "sku_02_02", - Quantity: 3, - Price: mustDecimalFromString("4"), - Currency: "USD", - Subtotal: mustDecimalFromString("12"), - CredentialType: "something", - }, - }, - }, - exp: tcExpected{ - expiresAt: time.Date(2023, time.April, 30, 1, 0, 0, 0, time.UTC), - }, - }, - } - - repo := repository.NewOrder() - iorepo := repository.NewOrderItem() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - order, err := createOrderForTest(ctx, tx, repo) - must.Equal(t, nil, err) - - if !tc.given.lastPaidAt.IsZero() { - err := repo.SetLastPaidAt(ctx, tx, order.ID, tc.given.lastPaidAt) - must.Equal(t, nil, err) - } - - if len(tc.given.items) > 0 { - model.OrderItemList(tc.given.items).SetOrderID(order.ID) - - _, err := iorepo.InsertMany(ctx, tx, tc.given.items...) - must.Equal(t, nil, err) - } - - actual, err := repo.GetExpiresAtAfterISOPeriod(ctx, tx, order.ID) - must.Equal(t, nil, err) - - // Handle the special case where last_paid_at was not set. - // The time is generated by the database, so it is non-deterministic. - if tc.given.lastPaidAt.IsZero() { - future, err := nowPlusIntervalPg(ctx, tx, "P1M") - must.Equal(t, nil, err) - - t.Log("actual", actual) - t.Log("future", future) - - diff := future.Sub(actual) - if diff < time.Duration(0) { - diff = actual.Sub(future) - } - - should.Equal(t, true, diff < time.Duration(1*time.Hour)) - return - } - - // TODO(pavelb): update local and testing containers to use Go 1.20+. - // Then switch to tc.exp.expiresAt.Compare(actual) == 0. - should.Equal(t, true, tc.exp.expiresAt.Sub(actual) == 0) - }) - } -} - -func TestOrder_CreateGet(t *testing.T) { - dbi, err := setupDBI() - must.Equal(t, nil, err) - - defer func() { - _, _ = dbi.Exec("TRUNCATE_TABLE orders;") - }() - - type tcGiven struct { - req *model.OrderNew - } - - type tcExpected struct { - result *model.Order - err error - } - - type testCase struct { - name string - given tcGiven - exp tcExpected - } - - tests := []testCase{ - { - name: "nil_allowed_payment_methods", - given: tcGiven{ - req: &model.OrderNew{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - TotalPrice: mustDecimalFromString("5"), - }, - }, - exp: tcExpected{ - result: &model.Order{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - }, - TotalPrice: mustDecimalFromString("5"), - }, - }, - }, - - { - name: "empty_allowed_payment_methods", - given: tcGiven{ - req: &model.OrderNew{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{}, - }, - }, - exp: tcExpected{ - result: &model.Order{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - }, - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{}, - }, - }, - }, - - { - name: "single_allowed_payment_methods", - given: tcGiven{ - req: &model.OrderNew{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{"stripe"}, - }, - }, - exp: tcExpected{ - result: &model.Order{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - }, - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{"stripe"}, - }, - }, - }, - - { - name: "many_allowed_payment_methods", - given: tcGiven{ - req: &model.OrderNew{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{"stripe", "cash"}, - }, - }, - exp: tcExpected{ - result: &model.Order{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - Location: datastore.NullString{ - NullString: sql.NullString{ - Valid: true, - String: "https://somewhere.brave.software", - }, - }, - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{"stripe", "cash"}, - }, - }, - }, - - { - name: "empty_location", - given: tcGiven{ - req: &model.OrderNew{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{"stripe"}, - }, - }, - exp: tcExpected{ - result: &model.Order{ - MerchantID: "brave.com", - Currency: "USD", - Status: "pending", - TotalPrice: mustDecimalFromString("5"), - AllowedPaymentMethods: pq.StringArray{"stripe"}, - }, - }, - }, - } - - repo := repository.NewOrder() - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - ctx := context.TODO() - - tx, err := dbi.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - must.Equal(t, nil, err) - - t.Cleanup(func() { _ = tx.Rollback() }) - - actual1, err := repo.Create(ctx, tx, tc.given.req) - must.Equal(t, true, errors.Is(err, tc.exp.err)) - - if tc.exp.err != nil { - return - } - - should.Equal(t, tc.exp.result.MerchantID, actual1.MerchantID) - should.Equal(t, tc.exp.result.Currency, actual1.Currency) - should.Equal(t, tc.exp.result.Status, actual1.Status) - should.Equal(t, tc.exp.result.Location.Valid, actual1.Location.Valid) - should.Equal(t, tc.exp.result.Location.String, actual1.Location.String) - should.Equal(t, true, tc.exp.result.TotalPrice.Equal(actual1.TotalPrice)) - should.Equal(t, tc.exp.result.AllowedPaymentMethods, actual1.AllowedPaymentMethods) - should.Equal(t, tc.exp.result.ValidFor, actual1.ValidFor) - - actual2, err := repo.Get(ctx, tx, actual1.ID) - must.Equal(t, nil, err) - - should.Equal(t, actual1.ID, actual2.ID) - should.Equal(t, actual1.MerchantID, actual2.MerchantID) - should.Equal(t, actual1.Currency, actual2.Currency) - should.Equal(t, actual1.Status, actual2.Status) - should.Equal(t, actual1.Location, actual2.Location) - should.Equal(t, true, actual1.TotalPrice.Equal(actual2.TotalPrice)) - should.Equal(t, actual1.AllowedPaymentMethods, actual2.AllowedPaymentMethods) - should.Equal(t, actual1.ValidFor, actual2.ValidFor) - should.Equal(t, actual1.CreatedAt, actual2.CreatedAt) - should.Equal(t, actual1.UpdatedAt, actual2.UpdatedAt) - }) - } -} - -func ptrString(s string) *string { - return &s -} - -func nowPlusInterval(v string) (time.Time, error) { - dur, err := timeutils.ParseDuration(v) - if err != nil { - return time.Time{}, err - } - - result, err := dur.FromNow() - if err != nil { - return time.Time{}, err - } - - return *result, nil -} - -func nowPlusIntervalPg(ctx context.Context, dbi sqlx.QueryerContext, v string) (time.Time, error) { - const q = `SELECT now() + $1::interval;` - - var result time.Time - if err := sqlx.GetContext(ctx, dbi, &result, q, v); err != nil { - return time.Time{}, err - } - - return result, nil -} diff --git a/services/skus/transaction.go b/services/skus/transaction.go deleted file mode 100644 index 08f95c756..000000000 --- a/services/skus/transaction.go +++ /dev/null @@ -1,21 +0,0 @@ -package skus - -import ( - "time" - - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// Transaction includes information about a particular order. Status can be pending, failure, completed, or error. -type Transaction struct { - ID uuid.UUID `json:"id" db:"id"` - OrderID uuid.UUID `json:"orderId" db:"order_id"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` - UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` - ExternalTransactionID string `json:"externalTransactionId" db:"external_transaction_id"` - Status string `json:"status" db:"status"` - Currency string `json:"currency" db:"currency"` - Kind string `json:"kind" db:"kind"` - Amount decimal.Decimal `json:"amount" db:"amount"` -} diff --git a/services/skus/vote.go b/services/skus/vote.go deleted file mode 100644 index c33a7b99c..000000000 --- a/services/skus/vote.go +++ /dev/null @@ -1,336 +0,0 @@ -package skus - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/clients/cbr" - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/jmoiron/sqlx" - "github.com/linkedin/goavro" - uuid "github.com/satori/go.uuid" - kafka "github.com/segmentio/kafka-go" - "github.com/shopspring/decimal" -) - -const ( - // UserWalletVoteSKU - special vote sku to denote user wallet funding - UserWalletVoteSKU string = "user-wallet-vote" - // AnonCardVoteSKU - special vote sku to denote anon-card funding - AnonCardVoteSKU = "anon-card-vote" -) - -var ( - // ErrInvalidSKUToken - the sku was invalid - ErrInvalidSKUToken = errors.New("failed to validate sku token") - // ErrInvalidSKUTokenSKU - the sku was invalid - ErrInvalidSKUTokenSKU = fmt.Errorf("invalid sku in sku token: %w", ErrInvalidSKUToken) - // ErrInvalidSKUTokenBadMerchant - the merchant in the sku is invalid - ErrInvalidSKUTokenBadMerchant = fmt.Errorf("invalid merchant id in sku token: %w", ErrInvalidSKUToken) -) - -// Vote encapsulates information from the browser about attention -type Vote struct { - Type string `json:"type" valid:"in(auto-contribute|oneoff-tip|recurring-tip)"` - Channel string `json:"channel" valid:"-"` - VoteTally int64 `json:"-" valid:"-"` - FundingSource string `json:"-" valid:"-"` -} - -// Validate - implement inputs.Validatable interface for input -func (v *Vote) Validate(ctx context.Context) error { - _, err := govalidator.ValidateStruct(v) - if err != nil { - return fmt.Errorf("failed vote structure validation: %w", err) - } - return nil -} - -// Decode - implement inputs.Decodable interface for input -func (v *Vote) Decode(ctx context.Context, input []byte) error { - err := v.Base64Decode(string(input)) - if err != nil { - return fmt.Errorf("error decoding vote: %w", err) - } - return nil -} - -// Base64Decode unmarshalls the vote from a string. -func (v *Vote) Base64Decode(text string) error { - var bytes []byte - bytes, err := base64.StdEncoding.DecodeString(text) - if err != nil { - return err - } - - err = json.Unmarshal(bytes, v) - return err -} - -/* - { - "type": "auto-contribute", - "channel": "coinmarketcap.com", - "voteTally": 20, - "fundingSource": "uphold" - } -*/ - -// VoteEvent encapsulates user and server provided information about a request to contribute kafka event -type VoteEvent struct { - Type string `json:"type"` - Channel string `json:"channel"` - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"createdAt"` - BaseVoteValue decimal.Decimal `json:"baseVoteValue"` - VoteTally int64 `json:"voteTally"` - FundingSource string `json:"fundingSource"` -} - -// NewVoteEvent - Create a new VoteEvent given a Vote -func NewVoteEvent(v Vote) (*VoteEvent, error) { - var ( - ve = &VoteEvent{ - ID: uuid.NewV4(), - Type: v.Type, - Channel: v.Channel, - CreatedAt: time.Now().UTC(), - VoteTally: v.VoteTally, - FundingSource: v.FundingSource, - } - err error - ) - // default base vote value - if ve.BaseVoteValue, err = decimal.NewFromString("0.25"); err != nil { - return nil, fmt.Errorf("failed to default BaseVoteValue: %w", err) - } - - return ve, nil -} - -// CodecEncode - encode using avro vote codec -func (ve *VoteEvent) CodecEncode(codec *goavro.Codec) ([]byte, error) { - return codec.BinaryFromNative(nil, map[string]interface{}{ - "type": ve.Type, - "channel": ve.Channel, - "id": ve.ID.String(), - "createdAt": ve.CreatedAt.Format(time.RFC3339), - "baseVoteValue": ve.BaseVoteValue.String(), - "voteTally": ve.VoteTally, - "fundingSource": ve.FundingSource, - }) -} - -// CodecDecode - Decode using avro vote codec -func (ve *VoteEvent) CodecDecode(codec *goavro.Codec, binary []byte) error { - native, _, err := codec.NativeFromBinary(binary) - if err != nil { - return errorutils.Wrap(err, "error decoding vote") - } - - // gross - v, err := json.Marshal(native) - if err != nil { - return fmt.Errorf("unable to marshal avro payload: %w", err) - } - - err = json.Unmarshal(v, ve) - if err != nil { - return fmt.Errorf("unable to decode decoded avro payload to VoteEvent: %w", err) - } - - return nil -} - -func rollbackTx(ds Datastore, tx *sqlx.Tx, wrap string, err error) error { - if pg, ok := ds.(*Postgres); ok { - if tx != nil { - // will handle logging to sentry if there is an error - pg.RollbackTx(tx) - } - } - return errorutils.Wrap(err, wrap) -} - -// RunNextVoteDrainJob - Attempt to drain the vote queue -func (s *Service) RunNextVoteDrainJob(ctx context.Context) (bool, error) { - logger := logging.Logger(ctx, "skus.RunNextVoteDrainJob") - - select { - case <-ctx.Done(): - // cancellation happened, kill this worker - logger.Error().Msg("cancellation envoked in drain vote queue!\n") - return false, nil - default: - - // make sure we are not paused. - if s.IsPaused() { - logger.Error().Msg("drain worker is paused!\n") - return false, nil - } - // pull vote from db queue - tx, records, err := s.Datastore.GetUncommittedVotesForUpdate(ctx) - if err != nil { - logger.Error().Err(err).Msg("failed to get uncommitted votes from drain queue") - return true, rollbackTx(s.Datastore, tx, "failed to get uncommitted votes from drain queue", err) - } - for _, record := range records { - if record == nil { - continue - } - var requestCredentials = []cbr.CredentialRedemption{} - err := json.Unmarshal([]byte(record.RequestCredentials), &requestCredentials) - if err != nil { - logger.Error().Err(err).Msg("failed to decode credentials") - if err := s.Datastore.MarkVoteErrored(ctx, *record, tx); err != nil { - logger.Error().Err(err).Msg("failed to mark vote as errored") - return true, rollbackTx(s.Datastore, tx, "failed to mark vote as errored for creds redemption", err) - } - // okay if it is errored, we will update the errored column - } - // redeem the credentials - err = s.cbClient.RedeemCredentials(ctx, requestCredentials, record.VoteText) - if err != nil { - logger.Error().Err(err).Msg("failed to redeem credentials") - if err := s.Datastore.MarkVoteErrored(ctx, *record, tx); err != nil { - return true, rollbackTx(s.Datastore, tx, "failed to mark vote as errored for creds redemption", err) - } - // okay if errored, update errored column - } - // write the message to kafka if successful - if err = s.kafkaWriter.WriteMessages(ctx, - kafka.Message{ - Topic: voteTopic, - Value: record.VoteEventBinary, - }, - ); err != nil { - if strings.Contains(err.Error(), "expired") { - // pause the worker for 30 minutes, expired cert - s.PauseWorker(time.Now().Add(30 * time.Minute)) - } - logger.Error().Err(err).Msg("failed to write message to kafka") - return true, rollbackTx(s.Datastore, tx, "failed to write vote to kafka", err) - } - // update the particular record to not be picked again - if err = s.Datastore.CommitVote(ctx, *record, tx); err != nil { - logger.Error().Err(err).Msg("failed to commit the vote") - return true, rollbackTx(s.Datastore, tx, "failed to commit vote to drain vote queue", err) - } - } - // finalize the record - if err := tx.Commit(); err != nil { - logger.Error().Err(err).Msg("failed to commit the transaction") - return true, fmt.Errorf("failed to commit transaction in drain vote queue: %w", err) - } - return true, nil - } -} - -// Vote based on the browser's attention -func (s *Service) Vote( - ctx context.Context, credentials []CredentialBinding, voteText string) error { - - logger := logging.Logger(ctx, "skus.Vote") - - var vote Vote - // decode and validate the inputs - if err := inputs.DecodeAndValidate(ctx, &vote, []byte(voteText)); err != nil { - return fmt.Errorf("error performing input decode/validate: %w", err) - } - - // generate all the cb credential redemptions - requestCredentials, err := generateCredentialRedemptions( - context.WithValue(ctx, appctx.DatastoreCTXKey, s.Datastore), credentials) - if err != nil { - return fmt.Errorf("error generating credential redemptions: %w", err) - } - - var credsByIssuer = map[string][]cbr.CredentialRedemption{} - - if len(requestCredentials) > 0 { - for _, rc := range requestCredentials { - // validate the issuer / sku of all credentials for validation - // we accept all the votes, or none of the votes. - merchantID, sku, err := decodeIssuerID(rc.Issuer) - if err != nil { - return fmt.Errorf("failed to decode issuer name for sku: %w", err) - } - - if merchantID != "brave.com" { - // validate that the merchantID is brave.com - // if not hard fail the request, and return an error stating the problem - logger.Warn().Str("merchantID", merchantID).Msg("merchantID should be brave.com, vote invalid") - return fmt.Errorf("merchant id != brave.com: %w", ErrInvalidSKUTokenBadMerchant) - } - - if sku != UserWalletVoteSKU && sku != AnonCardVoteSKU { - logger.Warn().Str("sku", sku).Msg("sku is invalid, should be user-wallet, or anonymous-card") - return fmt.Errorf("%s is an invalid sku: %w", sku, ErrInvalidSKUTokenSKU) - } - - // validation has completed. - credsByIssuer[rc.Issuer] = append(credsByIssuer[rc.Issuer], rc) - } - } - - // for each issuer we will create a vote with a particular vote tally - for k, v := range credsByIssuer { - vote.VoteTally = int64(len(v)) - // k holds the issuer name string, which has encoded in the funding source - // draw out the funding source and set it here. - _, sku, err := decodeIssuerID(k) - if err != nil { - return fmt.Errorf("failed to decode issuer name for sku: %w", err) - } - switch sku { - case UserWalletVoteSKU: - vote.FundingSource = "user-wallet" - case AnonCardVoteSKU: - vote.FundingSource = "anonymous-card" - default: - // should not get here, doing validation above on each issuer name - logger.Warn().Str("sku", sku).Msg("sku is invalid, should be user-wallet, or anonymous-card") - return fmt.Errorf("%s is an invalid sku: %w", sku, ErrInvalidSKUTokenSKU) - } - - // get a new VoteEvent to emit to kafka based on our input vote - voteEvent, err := NewVoteEvent(vote) - if err != nil { - return fmt.Errorf("failed to convert vote to kafka vote event: %w", err) - } - - // encode the event for processing - voteEventBinary, err := voteEvent.CodecEncode(s.codecs["vote"]) - if err != nil { - return fmt.Errorf("failed to encode avro codec: %w", err) - } - - rcSerial, err := json.Marshal(v) - if err != nil { - return fmt.Errorf("failed to encode request credentials for vote drain: %w", err) - } - - // insert serialized event into db - if err = s.Datastore.InsertVote( - ctx, VoteRecord{ - RequestCredentials: string(rcSerial), - VoteText: voteText, - VoteEventBinary: voteEventBinary, - }); err != nil { - return fmt.Errorf("datastore failure vote_drain: %w", err) - } - } - // at this point, after the vote is added to the database queue, we will let - // the service DrainVoteQueue handle the redemptions and kafka messages - - return nil -} diff --git a/services/skus/vote_test.go b/services/skus/vote_test.go deleted file mode 100644 index 43912474c..000000000 --- a/services/skus/vote_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package skus - -import ( - "context" - "database/sql/driver" - "encoding/base64" - "errors" - "strings" - "testing" - - sqlmock "github.com/DATA-DOG/go-sqlmock" - "github.com/jmoiron/sqlx" - - "github.com/brave-intl/bat-go/libs/clients/cbr" - "github.com/brave-intl/bat-go/libs/datastore" - kafkautils "github.com/brave-intl/bat-go/libs/kafka" - "github.com/brave-intl/bat-go/services/skus/storage/repository" -) - -type BytesContains []byte - -func (bc BytesContains) Match(v driver.Value) bool { - if b, ok := v.([]byte); ok { - return strings.Contains(string(b), string(bc)) - } - return false -} - -type StringContains string - -func (sc StringContains) Match(v driver.Value) bool { - if s, ok := v.(string); ok { - return strings.Contains(s, string(sc)) - } - return false -} - -// TestVoteAnonCard - given an issuer that is suffixed with "anon-card-vote" we should get a vote with funding source anon-card-vote -func TestVoteAnonCard(t *testing.T) { - var ( - issuerName = "brave.com?sku=anon-card-vote" - s = new(Service) - fakeGenerateCredentialRedemptions = func(ctx context.Context, cb []CredentialBinding) ([]cbr.CredentialRedemption, error) { - return []cbr.CredentialRedemption{ - { - Issuer: issuerName, - }, - }, nil - } - oldGenerateCredentialRedemptions = generateCredentialRedemptions - db, mock, _ = sqlmock.New() - err error - ) - // avro codecs - s.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "vote": voteSchema, - }) - if err != nil { - t.Error("failed to initialize avro codecs for test: ", err) - } - s.Datastore = Datastore( - &Postgres{ - Postgres: datastore.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - orderRepo: repository.NewOrder(), - orderItemRepo: repository.NewOrderItem(), - orderPayHistory: repository.NewOrderPayHistory(), - }, - ) - - defer func() { - if err := db.Close(); err != nil { - t.Log("failed to close mock database", err) - } - }() - voteText := base64.StdEncoding.EncodeToString([]byte(`{"channel":"brave.com", "type":"auto-contribute"}`)) - - // make sure vote_drain was updated - mock.ExpectExec("insert into vote_drain"). - WithArgs(StringContains(`issuer":"`+issuerName), voteText, BytesContains(`anonymous-card`)). - WillReturnResult(sqlmock.NewResult(1, 1)) - - generateCredentialRedemptions = fakeGenerateCredentialRedemptions - defer func() { - generateCredentialRedemptions = oldGenerateCredentialRedemptions - }() - - err = s.Vote( - context.Background(), []CredentialBinding{}, voteText) - if err != nil { - t.Error("encountered an error in Vote call: ", err.Error()) - } -} - -// TestVoteUnknownSKU - given an issuer that is suffixed with "anon-card-vote" we should get a vote with funding source anon-card-vote -func TestVoteUnknownSKU(t *testing.T) { - var ( - issuerName = "brave.com?sku=bad-sku" - s = new(Service) - fakeGenerateCredentialRedemptions = func(ctx context.Context, cb []CredentialBinding) ([]cbr.CredentialRedemption, error) { - return []cbr.CredentialRedemption{ - { - Issuer: issuerName, - }, - }, nil - } - oldGenerateCredentialRedemptions = generateCredentialRedemptions - err error - ) - // avro codecs - s.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "vote": voteSchema, - }) - if err != nil { - t.Error("failed to initialize avro codecs for test: ", err) - } - voteText := base64.StdEncoding.EncodeToString([]byte(`{"channel":"brave.com", "type":"auto-contribute"}`)) - - generateCredentialRedemptions = fakeGenerateCredentialRedemptions - defer func() { - generateCredentialRedemptions = oldGenerateCredentialRedemptions - }() - - err = s.Vote( - context.Background(), []CredentialBinding{}, voteText) - if err == nil { - t.Error("should have encountered an error in Vote call: ") - } - if !errors.Is(err, ErrInvalidSKUTokenSKU) { - t.Error("invalid error, should have gotten invalidskutokensku: ", err) - } -} - -// TestVoteUnknownBadMerch - given an issuer that is suffixed with "anon-card-vote" we should get a vote with funding source anon-card-vote -func TestVoteUnknownBadMerch(t *testing.T) { - var ( - issuerName = "notbrave.com?sku=anon-card-vote" - s = new(Service) - fakeGenerateCredentialRedemptions = func(ctx context.Context, cb []CredentialBinding) ([]cbr.CredentialRedemption, error) { - return []cbr.CredentialRedemption{ - { - Issuer: issuerName, - }, - }, nil - } - oldGenerateCredentialRedemptions = generateCredentialRedemptions - err error - ) - // avro codecs - s.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "vote": voteSchema, - }) - if err != nil { - t.Error("failed to initialize avro codecs for test: ", err) - } - voteText := base64.StdEncoding.EncodeToString([]byte(`{"channel":"brave.com", "type":"auto-contribute"}`)) - - generateCredentialRedemptions = fakeGenerateCredentialRedemptions - defer func() { - generateCredentialRedemptions = oldGenerateCredentialRedemptions - }() - - err = s.Vote( - context.Background(), []CredentialBinding{}, voteText) - if err == nil { - t.Error("should have encountered an error in Vote call: ") - } - if !errors.Is(err, ErrInvalidSKUTokenBadMerchant) { - t.Error("invalid error, should have gotten invalidskutokenbadmerchant: ", err) - } -} - -// TestVoteGoodAndBadSKU - one good credential, and one bad credential should result in no votes -func TestVoteGoodAndBadSKU(t *testing.T) { - var ( - issuerName1 = "brave.com?sku=bad-sku" - issuerName2 = "brave.com?sku=bad-sku" - s = new(Service) - fakeGenerateCredentialRedemptions = func(ctx context.Context, cb []CredentialBinding) ([]cbr.CredentialRedemption, error) { - return []cbr.CredentialRedemption{ - { - Issuer: issuerName1, - }, - { - Issuer: issuerName2, - }, - }, nil - } - oldGenerateCredentialRedemptions = generateCredentialRedemptions - err error - ) - // avro codecs - s.codecs, err = kafkautils.GenerateCodecs(map[string]string{ - "vote": voteSchema, - }) - if err != nil { - t.Error("failed to initialize avro codecs for test: ", err) - } - voteText := base64.StdEncoding.EncodeToString([]byte(`{"channel":"brave.com", "type":"auto-contribute"}`)) - - generateCredentialRedemptions = fakeGenerateCredentialRedemptions - defer func() { - generateCredentialRedemptions = oldGenerateCredentialRedemptions - }() - - err = s.Vote( - context.Background(), []CredentialBinding{}, voteText) - if err == nil { - t.Error("should have encountered an error in Vote call: ") - } - - if !errors.Is(err, ErrInvalidSKUTokenSKU) { - t.Error("invalid error, should have gotten invalidskutokensku: ", err) - } -} diff --git a/services/wallet/Dockerfile b/services/wallet/Dockerfile deleted file mode 100644 index 7b3c6ed12..000000000 --- a/services/wallet/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM bat-go:latest -CMD ["bat-go", "serve", "wallet", "rest"] diff --git a/services/wallet/README.md b/services/wallet/README.md deleted file mode 100644 index be6cc4271..000000000 --- a/services/wallet/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Wallet Microservice - -## Purpose - -To provide BAT-Wallet API. - -### to run - -go run ../main.go serve wallet rest - -## Custodian Linking Toggle - -We have the ability to turn off/on individually custodial linking capabilities -for rewards wallets. The following response can be expected for each of the -custodian linking endpoints if we have administratively turned off linking capabilities. - -### Uphold Linking Toggle - -``` -POST /v3/wallet/uphold//claim -... - -HTTP2/0 400 Bad Request -{ - "message": "Error validating Connecting Brave Rewards to Uphold is temporarily unavailable. Please try again later", - "code": 400, - "data": { - "validationErrors": null - } -} -``` - -### Gemini Linking Toggle - -``` -POST /v3/wallet/gemini//claim -... - -HTTP2/0 400 Bad Request -{ - "message": "Error validating Connecting Brave Rewards to Gemini is temporarily unavailable. Please try again later", - "code": 400, - "data": { - "validationErrors": null - } -} -``` - -### Bitflyer Linking Toggle - -``` -POST /v3/wallet/bitflyer//claim -... - -HTTP2/0 400 Bad Request -{ - "message": "Error validating Connecting Brave Rewards to Bitflyer is temporarily unavailable. Please try again later", - "code": 400, - "data": { - "validationErrors": null - } -} -``` diff --git a/services/wallet/cmd/rest_run.go b/services/wallet/cmd/rest_run.go deleted file mode 100644 index 940fc85bd..000000000 --- a/services/wallet/cmd/rest_run.go +++ /dev/null @@ -1,55 +0,0 @@ -package cmd - -import ( - "net/http" - "time" - - // pprof imports - _ "net/http/pprof" - - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/services/cmd" - "github.com/brave-intl/bat-go/services/wallet" - sentry "github.com/getsentry/sentry-go" - "github.com/go-chi/chi" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -// WalletRestRun - Main entrypoint of the REST subcommand -// This function takes a cobra command and starts up the -// wallets rest microservice. -func WalletRestRun(command *cobra.Command, args []string) { - ctx, service := wallet.SetupService(command.Context()) - router := cmd.SetupRouter(ctx) - wallet.RegisterRoutes(ctx, service, router) - - logger, err := appctx.GetLogger(ctx) - cmdutils.Must(err) - - // add profiling flag to enable profiling routes - if viper.GetString("pprof-enabled") != "" { - // pprof attaches routes to default serve mux - // host:6061/debug/pprof/ - go func() { - logger.Error().Err(http.ListenAndServe(":6061", http.DefaultServeMux)) - }() - } - - // setup server, and run - srv := http.Server{ - Addr: viper.GetString("address"), - Handler: chi.ServerBaseContext(ctx, router), - ReadTimeout: 5 * time.Second, - WriteTimeout: 20 * time.Second, - } - - // make sure exceptions go to sentry - defer sentry.Flush(time.Second * 2) - - if err = srv.ListenAndServe(); err != nil { - sentry.CaptureException(err) - logger.Fatal().Err(err).Msg("HTTP server start failed!") - } -} diff --git a/services/wallet/cmd/wallets.go b/services/wallet/cmd/wallets.go deleted file mode 100644 index 1628ee890..000000000 --- a/services/wallet/cmd/wallets.go +++ /dev/null @@ -1,58 +0,0 @@ -package cmd - -import ( - cmdutils "github.com/brave-intl/bat-go/cmd" - // pprof imports - _ "net/http/pprof" - - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/spf13/cobra" -) - -var ( - // WalletsCmd root wallets command - WalletsCmd = &cobra.Command{ - Use: "wallet", - Short: "provides wallets micro-service entrypoint", - } - - walletsRestCmd = &cobra.Command{ - Use: "rest", - Short: "provides REST api services", - Run: WalletRestRun, - } -) - -func init() { - WalletsCmd.AddCommand(walletsRestCmd) - - // add this command as a serve subcommand - rootcmd.RootCmd.AddCommand(WalletsCmd) - - // setup the flags - walletsCmdBuilder := cmdutils.NewFlagBuilder(WalletsCmd) - - walletsCmdBuilder.Flag().String("bitflyer-jwt-key", "", - "the bitflyer jwt key for validation of linking info"). - Env("BITFLYER_JWT_KEY"). - Bind("bitflyer-jwt-key"). - Require() - - walletsCmdBuilder.Flag().String("ro-datastore", "", - "the read only datastore for the wallet system"). - Env("RO_DATABASE_URL"). - Bind("ro-datastore"). - Require() - - walletsCmdBuilder.Flag().String("datastore", "", - "the datastore for the wallet system"). - Env("DATABASE_URL"). - Bind("datastore"). - Require() - - walletsCmdBuilder.Flag().Bool("enable-link-drain-flag", false, - "the in-migration flag disabling the wallets link feature"). - Env("ENABLE_LINKING_DRAINING"). - Bind("enable-link-drain-flag"). - Require() -} diff --git a/services/wallet/controllers_v3.go b/services/wallet/controllers_v3.go deleted file mode 100644 index a62da46d8..000000000 --- a/services/wallet/controllers_v3.go +++ /dev/null @@ -1,715 +0,0 @@ -package wallet - -import ( - "crypto" - "crypto/ed25519" - "database/sql" - "encoding/hex" - "errors" - "net/http" - "strings" - - "github.com/brave-intl/bat-go/libs/altcurrency" - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/go-chi/chi" - uuid "github.com/satori/go.uuid" -) - -// LinkDepositAccountResponse is the response returned by the linking endpoints. -type LinkDepositAccountResponse struct { - GeoCountry string `json:"geoCountry"` -} - -// CreateUpholdWalletV3 produces a http handler for the service which handles creation of uphold wallets. -func CreateUpholdWalletV3(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - ucReq = new(UpholdCreationRequest) - ctx = r.Context() - altCurrency = altcurrency.BAT - ) - - logger := logging.Logger(ctx, "wallet.CreateUpholdWalletV3") - - // decode and validate the request body - if err := inputs.DecodeAndValidateReader(ctx, ucReq, r.Body); err != nil { - return ucReq.HandleErrors(err) - } - - // get public key from the input signed Creation Request - var publicKey = ucReq.PublicKey - - // no more uphold wallets in the wild please - if env, ok := ctx.Value(appctx.EnvironmentCTXKey).(string); ok && env == "local" { - return handlers.WrapError( - errors.New("uphold wallet creation needs to be in an environment not local"), - "failed to create wallet", http.StatusBadRequest) - } - - var ( - db Datastore - ok bool - ) - - // get datastore from context - if db, ok = ctx.Value(appctx.DatastoreCTXKey).(Datastore); !ok { - logger.Error().Msg("unable to get datastore from context") - return handlers.WrapError(errors.New("unable to get datastore"), "misconfigured datastore", http.StatusServiceUnavailable) - } - - var info = &walletutils.Info{ - ID: uuid.NewV4().String(), - Provider: "uphold", - PublicKey: publicKey, - AltCurrency: &altCurrency, - } - - uwallet := uphold.Wallet{ - Info: *info, - PrivKey: ed25519.PrivateKey{}, - PubKey: httpsignature.Ed25519PubKey([]byte(publicKey)), - } - if err := uwallet.SubmitRegistration(ctx, ucReq.SignedCreationRequest); err != nil { - return handlers.WrapError( - errors.New("unable to create uphold wallet"), - "failed to register wallet with uphold", http.StatusServiceUnavailable) - } - info.ProviderID = uwallet.GetWalletInfo().ProviderID - - // get wallet from datastore - err := db.InsertWallet(ctx, info) - if err != nil { - logger.Error().Err(err).Str("id", info.ID).Msg("unable to create uphold wallet") - return handlers.WrapError(err, "error writing wallet to storage", http.StatusServiceUnavailable) - } - - return handlers.RenderContent(ctx, infoToResponseV3(info), w, http.StatusCreated) -} - -// CreateBraveWalletV3 - produces an http handler for the service s which handles creation of brave wallets -func CreateBraveWalletV3(w http.ResponseWriter, r *http.Request) *handlers.AppError { - verifier := httpsignature.ParameterizedKeystoreVerifier{ - SignatureParams: httpsignature.SignatureParams{ - Algorithm: httpsignature.ED25519, - Headers: []string{"digest", "(request-target)"}, - }, - Keystore: &DecodeEd25519Keystore{}, - Opts: crypto.Hash(0), - } - - // perform validation based on public key that the user submits - ctx, keyID, err := verifier.VerifyRequest(r) - if err != nil { - return handlers.WrapError(err, "invalid http signature", http.StatusForbidden) - } - - var ( - bcr = new(BraveCreationRequest) - ) - - // get logger from context - logger := logging.Logger(ctx, "wallet.CreateBraveWalletV3") - - if err := inputs.DecodeAndValidateReader(ctx, bcr, r.Body); err != nil { - return bcr.HandleErrors(err) - } - - var ( - db Datastore - ok bool - ) - - // get datastore from context - if db, ok = ctx.Value(appctx.DatastoreCTXKey).(Datastore); !ok { - logger.Error().Msg("unable to get datastore from context") - return handlers.WrapError(err, "misconfigured datastore", http.StatusServiceUnavailable) - } - - var altCurrency = altcurrency.BAT - - var info = &walletutils.Info{ - ID: uuid.NewV4().String(), - Provider: "brave", - PublicKey: keyID, - AltCurrency: &altCurrency, - } - - // get wallet from datastore - err = db.InsertWallet(ctx, info) - if err != nil { - logger.Error().Err(err).Str("id", info.ID).Msg("unable to create brave wallet") - return handlers.WrapError(err, "error writing wallet to storage", http.StatusServiceUnavailable) - } - - return handlers.RenderContent(ctx, infoToResponseV3(info), w, http.StatusCreated) -} - -// LinkBitFlyerDepositAccountV3 - produces an http handler for the service s which handles deposit account linking of uphold wallets -func LinkBitFlyerDepositAccountV3(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - ctx = r.Context() - id = new(inputs.ID) - blr = new(BitFlyerLinkingRequest) - ) - - l := logging.Logger(ctx, "wallet.CreateBitflyerWalletV3") - - // check if we have disabled bitflyer - if disableBitflyer, ok := ctx.Value(appctx.DisableBitflyerLinkingCTXKey).(bool); ok && disableBitflyer { - return handlers.ValidationError( - "Connecting Brave Rewards to Bitflyer is temporarily unavailable. Please try again later", - nil, - ) - } - - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - l.Warn().Err(err).Msg("failed to decode and validate paymentID from url") - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - // validate payment id matches what was in the http signature - signatureID, err := middleware.GetKeyID(r.Context()) - if err != nil { - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - if id.String() != signatureID { - return handlers.ValidationError( - "paymentId from URL does not match paymentId in http signature", - map[string]interface{}{ - "paymentID": "does not match http signature id", - }, - ) - } - - // read post body - if err := inputs.DecodeAndValidateReader(ctx, blr, r.Body); err != nil { - return blr.HandleErrors(err) - } - - country, err := s.LinkBitFlyerWallet(ctx, *id.UUID(), blr.DepositID, blr.AccountHash) - if err != nil { - l.Error().Err(err).Str("paymentID", id.String()).Msg("failed to link bitflyer wallet") - return handlers.WrapError(err, "error linking wallet", http.StatusBadRequest) - } - - return handlers.RenderContent(ctx, LinkDepositAccountResponse{ - GeoCountry: country, - }, w, http.StatusOK) - } -} - -// LinkZebPayDepositAccountV3 returns a handler which handles deposit account linking of zebpay wallets. -func LinkZebPayDepositAccountV3(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - - // Check whether it's disabled. - if disable, ok := ctx.Value(appctx.DisableZebPayLinkingCTXKey).(bool); ok && disable { - const msg = "Connecting Brave Rewards to ZebPay is temporarily unavailable. Please try again later" - return handlers.ValidationError(msg, nil) - } - - id := &inputs.ID{} - l := logging.Logger(ctx, "wallet.LinkZebPayDepositAccountV3") - - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - l.Warn().Str("paymentID", err.Error()).Msg("failed to decode and validate paymentID from url") - - const msg = "error validating paymentID url parameter" - return handlers.ValidationError(msg, map[string]interface{}{"paymentID": err.Error()}) - } - - // Check that payment id matches what was in the http signature. - signatureID, err := middleware.GetKeyID(ctx) - if err != nil { - const msg = "error validating paymentID url parameter" - return handlers.ValidationError(msg, map[string]interface{}{"paymentID": err.Error()}) - } - - if id.String() != signatureID { - const msg = "paymentId from URL does not match paymentId in http signature" - return handlers.ValidationError(msg, map[string]interface{}{ - "paymentID": "does not match http signature id", - }) - } - - zplReq := &ZebPayLinkingRequest{} - if err := inputs.DecodeAndValidateReader(ctx, zplReq, r.Body); err != nil { - return HandleErrorsZebPay(err) - } - - country, err := s.LinkZebPayWallet(ctx, *id.UUID(), zplReq.VerificationToken) - if err != nil { - l.Error().Err(err).Str("paymentID", id.String()).Msg("failed to link wallet") - switch { - case errors.Is(err, errorutils.ErrInvalidCountry): - return handlers.WrapError(err, "region not supported", http.StatusBadRequest) - case errors.Is(err, errZPInvalidKYC): - return handlers.WrapError(err, "KYC required", http.StatusForbidden) - default: - return handlers.WrapError(err, err.Error(), http.StatusBadRequest) - } - } - - return handlers.RenderContent(ctx, LinkDepositAccountResponse{ - GeoCountry: country, - }, w, http.StatusOK) - } -} - -// LinkGeminiDepositAccountV3 returns an HTTP handler which is responsible for linking a Gemini wallet. -// This endpoint expects a walletID as part of the URL and takes a verification token which encodes the -// linking information as well as a recipientID. The recipientID is synonymous with a wallets depositID. -func LinkGeminiDepositAccountV3(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - ctx = r.Context() - id = new(inputs.ID) - glr = new(GeminiLinkingRequest) - ) - - l := logging.Logger(ctx, "wallet.LinkGeminiDepositAccountV3") - - // check if we have disabled Gemini - if disableGemini, ok := ctx.Value(appctx.DisableGeminiLinkingCTXKey).(bool); ok && disableGemini { - return handlers.ValidationError( - "Connecting Brave Rewards to Gemini is temporarily unavailable. Please try again later", - nil, - ) - } - - // get payment id - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - l.Warn().Str("paymentID", id.String()).Err(err). - Msg("failed to decode and validate paymentID from url") - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - // validate payment id matches what was in the http signature - signatureID, err := middleware.GetKeyID(ctx) - if err != nil { - l.Warn().Str("paymentID", id.String()). - Err(err).Msg("could not get http signing key id from context") - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - if id.String() != signatureID { - l.Warn().Str("paymentID", id.String()). - Msg("id does not match signature id") - return handlers.ValidationError( - "paymentId from URL does not match paymentId in http signature", - map[string]interface{}{ - "paymentID": "does not match http signature id", - }, - ) - } - - // read post body - if err := inputs.DecodeAndValidateReader(ctx, glr, r.Body); err != nil { - l.Warn().Str("paymentID", id.String()). - Err(err).Msg("could not validate request") - return glr.HandleErrors(err) - } - - country, err := s.LinkGeminiWallet(ctx, *id.UUID(), glr.VerificationToken, glr.DepositID) - if err != nil { - l.Error().Str("paymentID", id.String()). - Err(err).Msg("error linking gemini wallet") - - if errors.Is(err, errorutils.ErrInvalidCountry) { - return handlers.WrapError(err, "region not supported", http.StatusBadRequest) - } - - return handlers.WrapError(err, "error linking wallet", http.StatusBadRequest) - } - - return handlers.RenderContent(ctx, LinkDepositAccountResponse{ - GeoCountry: country, - }, w, http.StatusOK) - } -} - -// LinkUpholdDepositAccountV3 - produces an http handler for the service s which handles deposit account linking of uphold wallets -func LinkUpholdDepositAccountV3(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - ctx = r.Context() - id = new(inputs.ID) - cuw = new(LinkUpholdDepositAccountRequest) - ) - // get logger from context - l := logging.Logger(ctx, "wallet.LinkUpholdDepositAccountV3") - - // check if we have disabled uphold - if disableUphold, ok := ctx.Value(appctx.DisableUpholdLinkingCTXKey).(bool); ok && disableUphold { - return handlers.ValidationError( - "Connecting Brave Rewards to Uphold is temporarily unavailable. Please try again later", - nil, - ) - } - - // get payment id - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - l.Warn().Str("paymentID", err.Error()).Msg("failed to decode and validate paymentID from url") - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - // read post body - if err := inputs.DecodeAndValidateReader(ctx, cuw, r.Body); err != nil { - return cuw.HandleErrors(err) - } - - // get the wallet - wallet, err := s.GetWallet(ctx, *id.UUID()) - if err != nil { - if strings.Contains(err.Error(), "looking up wallet") { - return handlers.WrapError(err, "unable to find wallet", http.StatusNotFound) - } - return handlers.WrapError(err, "unable to get or create wallets", http.StatusServiceUnavailable) - } - - var aa uuid.UUID - - if cuw.AnonymousAddress != "" { - aa, err = uuid.FromString(cuw.AnonymousAddress) - if err != nil { - return handlers.WrapError(err, "error parsing anonymous address", http.StatusBadRequest) - } - } - - publicKey, err := hex.DecodeString(wallet.PublicKey) - if err != nil { - l.Warn().Err(err).Msg("unable to decode wallet public key") - return handlers.WrapError(errors.New("unable to decode wallet public key"), - "unable to decode wallet public key for creation request validation", - http.StatusInternalServerError) - } - uwallet := uphold.Wallet{ - Info: *wallet, - PrivKey: ed25519.PrivateKey{}, - PubKey: httpsignature.Ed25519PubKey([]byte(publicKey)), - } - - country, err := s.LinkUpholdWallet(ctx, uwallet, cuw.SignedLinkingRequest, &aa) - if err != nil { - l.Error().Err(err).Str("paymentID", id.String()). - Msg("failed to link wallet") - if errors.Is(err, errorutils.ErrInvalidCountry) { - return handlers.WrapError(err, "region not supported", http.StatusBadRequest) - } - return handlers.WrapError(err, "error linking wallet", http.StatusBadRequest) - } - - // render the wallet - return handlers.RenderContent(ctx, LinkDepositAccountResponse{ - GeoCountry: country, - }, w, http.StatusOK) - } -} - -// GetWalletV3 - produces an http handler for the service s which handles getting of brave wallets -func GetWalletV3(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ctx = r.Context() - // get logger from context - logger := logging.Logger(ctx, "wallet.GetWalletV3") - - var id = new(inputs.ID) - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - logger.Warn().Str("paymentId", err.Error()).Msg("failed to decode and validate paymentID from url") - return handlers.ValidationError( - "Error validating paymentID url parameter", - map[string]interface{}{ - "paymentId": err.Error(), - }, - ) - } - - var ( - roDB ReadOnlyDatastore - ok bool - ) - - // get datastore from context - if roDB, ok = ctx.Value(appctx.RODatastoreCTXKey).(ReadOnlyDatastore); !ok { - logger.Error().Msg("unable to get read only datastore from context") - } - - // get wallet from datastore - info, err := roDB.GetWallet(ctx, *id.UUID()) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - logger.Info().Err(err).Str("id", id.String()).Msg("wallet not found") - return handlers.WrapError(err, "no such wallet", http.StatusNotFound) - } - logger.Warn().Err(err).Str("id", id.String()).Msg("unable to get wallet") - return handlers.WrapError(err, "error getting wallet from storage", http.StatusInternalServerError) - } - if info == nil { - logger.Info().Err(err).Str("id", id.String()).Msg("wallet not found") - return handlers.WrapError(err, "no such wallet", http.StatusNotFound) - } - - // render the wallet - return handlers.RenderContent(ctx, infoToResponseV3(info), w, http.StatusOK) -} - -// RecoverWalletV3 - produces an http handler for the service s which handles recovering of brave wallets -func RecoverWalletV3(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ctx = r.Context() - // get logger from context - logger := logging.Logger(ctx, "wallet.RecoverWalletV3") - - var pk = new(inputs.PublicKey) - if err := inputs.DecodeAndValidateString(ctx, pk, chi.URLParam(r, "publicKey")); err != nil { - logger.Warn().Str("publicKey", err.Error()).Msg("failed to decode and validate publicKey from url") - return handlers.ValidationError( - "Error validating publicKey url parameter", - map[string]interface{}{ - "publicKey": err.Error(), - }, - ) - } - - var ( - roDB ReadOnlyDatastore - ok bool - ) - - // get datastore from context - if roDB, ok = ctx.Value(appctx.RODatastoreCTXKey).(ReadOnlyDatastore); !ok { - logger.Error().Msg("unable to get read only datastore from context") - } - - // get wallet from datastore - info, err := roDB.GetWalletByPublicKey(ctx, pk.String()) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - logger.Info().Err(err).Str("pk", pk.String()).Msg("wallet not found") - return handlers.WrapError(err, "no such wallet", http.StatusNotFound) - } - logger.Warn().Err(err).Str("id", pk.String()).Msg("unable to get wallet") - return handlers.WrapError(err, "error getting wallet from storage", http.StatusBadGateway) - } - if info == nil { - logger.Info().Err(err).Str("id", pk.String()).Msg("wallet not found") - return handlers.WrapError(err, "no such wallet", http.StatusNotFound) - } - - // render the wallet - return handlers.RenderContent(ctx, infoToResponseV3(info), w, http.StatusOK) -} - -// GetUpholdWalletBalanceV3 - produces an http handler for the service s which handles balance inquiries of uphold wallets -func GetUpholdWalletBalanceV3(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ctx = r.Context() - // get logger from context - logger := logging.Logger(ctx, "wallet.GetUpholdWalletBalanceV3") - // get the payment id from the URL request - var id = new(inputs.ID) - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - logger.Warn().Str("paymentID", err.Error()).Msg("failed to decode and validate payment id from url") - return handlers.ValidationError( - "Error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - var ( - roDB ReadOnlyDatastore - ok bool - ) - - // get datastore from context - if roDB, ok = ctx.Value(appctx.RODatastoreCTXKey).(ReadOnlyDatastore); !ok { - logger.Error().Msg("unable to get read only datastore from context") - } - - // get wallet from datastore - info, err := roDB.GetWallet(ctx, *id.UUID()) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - logger.Info().Err(err).Str("id", id.String()).Msg("wallet not found") - return handlers.WrapError(err, "no such wallet", http.StatusNotFound) - } - logger.Warn().Err(err).Str("id", id.String()).Msg("unable to get wallet") - return handlers.WrapError(err, "error getting wallet from storage", http.StatusInternalServerError) - } - if info == nil { - logger.Info().Err(err).Str("id", id.String()).Msg("wallet not found") - return handlers.WrapError(err, "no such wallet", http.StatusNotFound) - } - - if info.Provider != "uphold" { - // not anoncard wallet, invalid - logger.Warn().Str("id", id.String()).Msg("wallet not capable of balance inquiry") - return handlers.WrapError(err, "wallet not capable of balance inquiry", http.StatusBadRequest) - } else if info.ProviderID == "" { // implied only for uphold - return handlers.WrapError(errors.New("provider id does not exist"), "wallet not capable of balance inquiry", http.StatusForbidden) - } - - // convert this wallet to an uphold wallet - uwallet := uphold.Wallet{ - Info: *info, - } - - // get the wallet balance - result, err := uwallet.GetBalance(ctx, true) - if err != nil { - logger.Info().Err(err).Str("id", id.String()).Msg("error getting balance from uphold") - return handlers.WrapError(err, "failed to get balance from uphold", http.StatusInternalServerError) - } - - // format the response and render - return handlers.RenderContent(ctx, balanceToResponseV3(*result), w, http.StatusOK) -} - -// GetLinkingInfoV3 - get linking metadata -func GetLinkingInfoV3(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - ctx = r.Context() - paymentID = new(inputs.ID) - // default to a uuid that doesn't exist - providerLinkingID = uuid.NewV4().String() - custodianID = r.URL.Query().Get("custodianId") - ) - // get logger from context - logger := logging.Logger(ctx, "wallet.GetLinkingInfoV3") - - if r.URL.Query().Get("paymentId") != "" { - // get payment id - if err := inputs.DecodeAndValidateString(ctx, paymentID, r.URL.Query().Get("paymentId")); err != nil { - logger.Warn().Err(err).Str("paymentID", r.URL.Query().Get("paymentId")).Msg("failed to decode and validate paymentID from url") - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - // get the wallet - wallet, err := s.GetWallet(ctx, *paymentID.UUID()) - if err != nil || wallet == nil { - if wallet == nil || strings.Contains(err.Error(), "looking up wallet") { - return handlers.WrapError(err, "unable to find wallet", http.StatusNotFound) - } - return handlers.WrapError(err, "unable to get linking limit for payment id", http.StatusServiceUnavailable) - } - if wallet.ProviderLinkingID != nil { - providerLinkingID = wallet.ProviderLinkingID.String() - } - } - - info, err := s.GetLinkingInfo(ctx, providerLinkingID, custodianID) - if err != nil { - logger.Error().Err(err).Str("custodianId", custodianID).Msg("failed to get linking info") - return handlers.WrapError(err, "error getting linking info", http.StatusBadRequest) - } - - return handlers.RenderContent(ctx, info, w, http.StatusOK) - } -} - -// DisconnectCustodianLinkV3 - produces an http handler for the service s which handles disconnect -// state for a deposit account linking -func DisconnectCustodianLinkV3(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - var ( - ctx = r.Context() - id = new(inputs.ID) - custodian = new(CustodianName) - ) - // get logger from context - logger := logging.Logger(ctx, "wallet.DisconnectCustodianLinkV3") - - // get payment id - if err := inputs.DecodeAndValidateString(ctx, id, chi.URLParam(r, "paymentID")); err != nil { - logger.Warn().Str("paymentID", err.Error()).Msg("failed to decode and validate paymentID from url") - return handlers.ValidationError( - "error validating paymentID url parameter", - map[string]interface{}{ - "paymentID": err.Error(), - }, - ) - } - - // get custodian name - if err := inputs.DecodeAndValidateString(ctx, custodian, chi.URLParam(r, "custodian")); err != nil { - logger.Warn().Str("custodian", err.Error()).Msg("failed to decode and validate custodian from url") - return handlers.ValidationError( - "error validating custodian url parameter", - map[string]interface{}{ - "custodian": err.Error(), - }, - ) - } - - sublogger := logger.With(). - Str("custodian", custodian.String()). - Str("paymentID", id.String()).Logger() - - // validate payment id matches what was in the http signature - signatureID, err := middleware.GetKeyID(r.Context()) - if err != nil { - return handlers.ValidationError( - "error validating http signature, does not match paymentID url parameter", - map[string]interface{}{ - "signature": err.Error(), - }, - ) - } - - if id.String() != signatureID { - return handlers.ValidationError( - "paymentId from URL does not match paymentId in http signature", - map[string]interface{}{ - "paymentID": "does not match http signature id", - }, - ) - } - - err = s.DisconnectCustodianLink(ctx, custodian.String(), *id.UUID()) - if err != nil { - sublogger.Error().Err(err).Msg("failed to disconnect custodian link") - return handlers.WrapError(err, "failed to disconnect custodian link", http.StatusInternalServerError) - } - - return handlers.RenderContent(ctx, map[string]interface{}{}, w, http.StatusOK) - } -} diff --git a/services/wallet/controllers_v3_test.go b/services/wallet/controllers_v3_test.go deleted file mode 100644 index 9179ead76..000000000 --- a/services/wallet/controllers_v3_test.go +++ /dev/null @@ -1,1135 +0,0 @@ -package wallet_test - -import ( - "bytes" - "context" - "crypto" - "crypto/ed25519" - "crypto/sha256" - "database/sql" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/DATA-DOG/go-sqlmock" - "github.com/brave-intl/bat-go/libs/clients/gemini" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - mockgemini "github.com/brave-intl/bat-go/libs/clients/gemini/mock" - mockreputation "github.com/brave-intl/bat-go/libs/clients/reputation/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - datastoreutils "github.com/brave-intl/bat-go/libs/datastore" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/brave-intl/bat-go/services/wallet" - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" - "gopkg.in/square/go-jose.v2" - "gopkg.in/square/go-jose.v2/jwt" -) - -func TestCreateBraveWalletV3(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - var ( - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - // add the datastore to the context - ctx = context.Background() - handler = wallet.CreateBraveWalletV3 - r = httptest.NewRequest("POST", "/v3/wallet/brave", nil) - ) - // no logger, setup - ctx, _ = logging.SetupLogger(ctx) - - // setup sqlmock - mock.ExpectExec("^INSERT INTO wallets (.+)").WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).WillReturnResult(result{}) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - // setup keypair - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - must(t, "failed to generate keypair", err) - - err = signRequest(r, publicKey, privKey) - must(t, "failed to sign request", err) - - r = r.WithContext(ctx) - - var rw = httptest.NewRecorder() - handlers.AppHandler(handler).ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusCreated, rw.Code, string(b)) -} - -func TestCreateUpholdWalletV3(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - var ( - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - // add the datastore to the context - ctx = context.Background() - handler = wallet.CreateUpholdWalletV3 - r = httptest.NewRequest("POST", "/v3/wallet/uphold", bytes.NewBufferString(`{ - "signedCreationRequest": "eyJib2R5Ijp7ImRlbm9taW5hdGlvbiI6eyJhbW91bnQiOiIwIiwiY3VycmVuY3kiOiJCQVQifSwiZGVzdGluYXRpb24iOiJhNmRmZjJiYS1kMGQxLTQxYzQtOGU1Ni1hMjYwNWJjYWY0YWYifSwiaGVhZGVycyI6eyJkaWdlc3QiOiJTSEEtMjU2PWR2RTAzVHdpRmFSR0c0MUxLSkR4aUk2a3c5M0h0cTNsclB3VllldE5VY1E9Iiwic2lnbmF0dXJlIjoia2V5SWQ9XCJwcmltYXJ5XCIsYWxnb3JpdGhtPVwiZWQyNTUxOVwiLGhlYWRlcnM9XCJkaWdlc3RcIixzaWduYXR1cmU9XCJkcXBQdERESXE0djNiS1V5eHB6Q3Vyd01nSzRmTWk1MUJjakRLc2pTak90K1h1MElZZlBTMWxEZ01aRkhiaWJqcGh0MVd3V3l5enFad3lVNW0yN1FDUT09XCIifSwib2N0ZXRzIjoie1wiZGVub21pbmF0aW9uXCI6e1wiYW1vdW50XCI6XCIwXCIsXCJjdXJyZW5jeVwiOlwiQkFUXCJ9LFwiZGVzdGluYXRpb25cIjpcImE2ZGZmMmJhLWQwZDEtNDFjNC04ZTU2LWEyNjA1YmNhZjRhZlwifSJ9"}`)) - ) - // no logger, setup - ctx, _ = logging.SetupLogger(ctx) - - // setup sqlmock - mock.ExpectExec("^INSERT INTO wallets (.+)").WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).WillReturnResult(result{}) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - r = r.WithContext(ctx) - - var rw = httptest.NewRecorder() - handlers.AppHandler(handler).ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusBadRequest, rw.Code, string(b)) -} - -func TestGetWalletV3(t *testing.T) { - var ( - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - roDatastore = wallet.ReadOnlyDatastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - // add the datastore to the context - ctx = context.Background() - id = uuid.NewV4() - r = httptest.NewRequest("GET", fmt.Sprintf("/v3/wallet/%s", id), nil) - handler = wallet.GetWalletV3 - rw = httptest.NewRecorder() - rows = sqlmock.NewRows([]string{"id", "provider", "provider_id", "public_key", "provider_linking_id", "anonymous_address"}). - AddRow(id, "brave", "", "12345", id, id) - ) - - mock.ExpectQuery("^select (.+)").WithArgs(id).WillReturnRows(rows) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.RODatastoreCTXKey, roDatastore) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Get("/v3/wallet/{paymentID}", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) -} - -func TestLinkBitFlyerWalletV3(t *testing.T) { - wallet.VerifiedWalletEnable = true - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - // setup jwt token for the test - var secret = []byte("a jwt secret") - sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: secret}, (&jose.SignerOptions{}).WithType("JWT")) - if err != nil { - panic(err) - } - - var ( - idFrom = uuid.NewV4() - idTo = uuid.NewV4() - accountHash = uuid.NewV4() - timestamp = time.Now() - ) - - h := sha256.New() - if _, err := h.Write([]byte(idFrom.String())); err != nil { - panic(err) - } - - externalAccountID := hex.EncodeToString(h.Sum(nil)) - - linkingInfo := wallet.BitFlyerLinkingInfo{ - DepositID: idTo.String(), - RequestID: "1", - AccountHash: accountHash.String(), - ExternalAccountID: externalAccountID, - Timestamp: timestamp, - } - - tokenString, err := jwt.Signed(sig).Claims(linkingInfo).CompactSerialize() - if err != nil { - panic(err) - } - - var ( - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - - // add the datastore to the context - ctx = middleware.AddKeyID(context.WithValue(context.Background(), appctx.BitFlyerJWTKeyCTXKey, []byte(secret)), idFrom.String()) - r = httptest.NewRequest( - "POST", - fmt.Sprintf("/v3/wallet/bitflyer/%s/claim", idFrom), - bytes.NewBufferString(fmt.Sprintf(` - { - "linkingInfo": "%s" - }`, tokenString)), - ) - mockReputation = mockreputation.NewMockClient(mockCtrl) - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, nil, nil) - handler = wallet.LinkBitFlyerDepositAccountV3(s) - rw = httptest.NewRecorder() - ) - - mock.ExpectExec("^insert (.+)").WithArgs("1").WillReturnResult(sqlmock.NewResult(1, 1)) - - mockSQLCustodianLink(mock, "bitflyer") - - // begin linking tx - mock.ExpectBegin() - - // make sure old linking id matches new one for same custodian - linkingID := uuid.NewV5(wallet.ClaimNamespace, accountHash.String()) - - // acquire lock for linkingID - mock.ExpectExec("^SELECT pg_advisory_xact_lock\\(hashtext(.+)\\)").WithArgs(linkingID.String()). - WillReturnResult(sqlmock.NewResult(1, 1)) - - // this wallet has been linked prior, with the same linking id that the request is with - // SHOULD SKIP THE linking limit checks - var linkingIDRows = sqlmock.NewRows([]string{"linking_id"}).AddRow(linkingID) - mock.ExpectQuery("^select linking_id from (.+)").WithArgs(idFrom, "bitflyer").WillReturnRows(linkingIDRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallet_custodian (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - clRows := sqlmock.NewRows([]string{"created_at", "linked_at"}). - AddRow(time.Now(), time.Now()) - - // insert into wallet custodian - mock.ExpectQuery("^insert into wallet_custodian (.+)").WithArgs(idFrom, "bitflyer", uuid.NewV5(wallet.ClaimNamespace, accountHash.String())).WillReturnRows(clRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idTo, linkingID, "bitflyer", idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectExec("^insert into (.+)").WithArgs(idFrom, true).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction - mock.ExpectCommit() - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, mockReputation) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - mockReputation.EXPECT().IsLinkingReputable( - gomock.Any(), // ctx - gomock.Any(), // wallet id - gomock.Any(), // country - ).Return( - true, - []int{}, - nil, - ) - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Post("/v3/wallet/bitflyer/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) - - var l wallet.LinkDepositAccountResponse - err = json.Unmarshal(b, &l) - require.NoError(t, err) - - assert.Equal(t, "JP", l.GeoCountry) -} - -func TestLinkGeminiWalletV3RelinkBadRegion(t *testing.T) { - wallet.VerifiedWalletEnable = true - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - var ( - // setup test variables - idFrom = uuid.NewV4() - ctx = middleware.AddKeyID(context.Background(), idFrom.String()) - accountID = uuid.NewV4() - idTo = accountID - - // setup db mocks - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - linkingInfo = "this is the fake jwt for linking_info" - - // setup mock clients - mockReputationClient = mockreputation.NewMockClient(mockCtrl) - mockGeminiClient = mockgemini.NewMockClient(mockCtrl) - - // this is our main request - r = httptest.NewRequest( - "POST", - fmt.Sprintf("/v3/wallet/gemini/%s/claim", idFrom), - bytes.NewBufferString(fmt.Sprintf(` - { - "linking_info": "%s", - "recipient_id": "%s" - }`, linkingInfo, idTo)), - ) - - mtc = &mockMtc{} - gem = &mockGemini{ - fnGetIssuingCountry: func(acc gemini.ValidatedAccount, fallback bool) string { - return "US" - }, - } - - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, mtc, gem) - handler = wallet.LinkGeminiDepositAccountV3(s) - rw = httptest.NewRecorder() - ) - - mockReputationClient.EXPECT().IsLinkingReputable( - gomock.Any(), // ctx - gomock.Any(), // wallet id - gomock.Any(), // country - ).Return( - true, - []int{}, - nil, - ) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, mockReputationClient) - ctx = context.WithValue(ctx, appctx.GeminiClientCTXKey, mockGeminiClient) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - // turn on region check - ctx = context.WithValue(ctx, appctx.UseCustodianRegionsCTXKey, true) - // configure allow region - custodianRegions := custodian.Regions{ - Gemini: custodian.GeoAllowBlockMap{ - Allow: []string{"US"}, - }, - } - ctx = context.WithValue(ctx, appctx.CustodianRegionsCTXKey, custodianRegions) - - validateAccountRes := gemini.ValidatedAccount{ - ID: accountID.String(), - ValidDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "US", - }, - }, - } - - mockGeminiClient.EXPECT().FetchValidateAccount( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return(validateAccountRes, nil) - - mockSQLCustodianLink(mock, "gemini") - - // begin linking tx - mock.ExpectBegin() - - // make sure old linking id matches new one for same custodian - linkingID := uuid.NewV5(wallet.ClaimNamespace, idTo.String()) - - // acquire lock for linkingID - mock.ExpectExec("^SELECT pg_advisory_xact_lock\\(hashtext(.+)\\)").WithArgs(linkingID.String()). - WillReturnResult(sqlmock.NewResult(1, 1)) - - // not before linked - mock.ExpectQuery("^select linking_id from (.+)").WithArgs(idFrom, "gemini").WillReturnError(sql.ErrNoRows) - - var max = sqlmock.NewRows([]string{"max"}).AddRow(4) - var open = sqlmock.NewRows([]string{"used"}).AddRow(0) - - var custLinks = sqlmock.NewRows([]string{"custodian", "linking_id"}).AddRow("gemini", linkingID.String()) - - // linking limit checks - mock.ExpectQuery("^select wc1.custodian, wc1.linking_id from wallet_custodian (.+)").WithArgs(linkingID).WillReturnRows(custLinks) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID, 4).WillReturnRows(max) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID).WillReturnRows(open) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID).WillReturnRows(sqlmock.NewRows([]string{"wallet_id"}).AddRow(uuid.NewV4().String())) - // get last un linking - var lastUnlink = sqlmock.NewRows([]string{"last_unlinking"}).AddRow(time.Now()) - mock.ExpectQuery("^select max(.+)").WithArgs(linkingID).WillReturnRows(lastUnlink) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallet_custodian (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - clRows := sqlmock.NewRows([]string{"created_at", "linked_at"}). - AddRow(time.Now(), time.Now()) - - // insert into wallet custodian - mock.ExpectQuery("^insert into wallet_custodian (.+)").WithArgs(idFrom, "gemini", uuid.NewV5(wallet.ClaimNamespace, accountID.String())).WillReturnRows(clRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idTo, linkingID, "gemini", idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectExec("^insert into (.+)").WithArgs(idFrom, true).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction - mock.ExpectCommit() - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Post("/v3/wallet/gemini/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) - - var l wallet.LinkDepositAccountResponse - err := json.Unmarshal(b, &l) - require.NoError(t, err) - - assert.Equal(t, "US", l.GeoCountry) - - // delete linking - r = httptest.NewRequest( - "DELETE", - fmt.Sprintf("/v3/wallet/gemini/%s/claim", idFrom), nil) - - handler = wallet.DisconnectCustodianLinkV3(s) - rw = httptest.NewRecorder() - - // create transaction - mock.ExpectBegin() - - // removes the link to the user_deposit_destination record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // updates the disconnected date on the record, and returns no error and one changed row - mock.ExpectExec("^update wallet_custodian(.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction because we are done disconnecting - mock.ExpectCommit() - - r = r.WithContext(ctx) - - router = chi.NewRouter() - router.Delete("/v3/wallet/{custodian}/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - if resp := rw.Result(); resp.StatusCode != http.StatusOK { - must(t, "invalid response", fmt.Errorf("expected %d, got %d", http.StatusOK, resp.StatusCode)) - } - - // ban the country now - custodianRegions = custodian.Regions{ - Gemini: custodian.GeoAllowBlockMap{ - Allow: []string{}, - }, - } - ctx = context.WithValue(ctx, appctx.CustodianRegionsCTXKey, custodianRegions) - - // begin linking tx - mock.ExpectBegin() - - // acquire lock for linkingID - mock.ExpectExec("^SELECT pg_advisory_xact_lock\\(hashtext(.+)\\)").WithArgs(linkingID.String()). - WillReturnResult(sqlmock.NewResult(1, 1)) - - // not before linked - mock.ExpectQuery("^select linking_id from (.+)").WithArgs(idFrom, "gemini").WillReturnError(sql.ErrNoRows) - - // perform again, make sure we check haslinkedprio - hasPriorRows := sqlmock.NewRows([]string{"result"}). - AddRow(true) - mock.ExpectQuery("^select exists(select 1 from wallet_custodian (.+)").WithArgs(uuid.NewV5(wallet.ClaimNamespace, accountID.String()), idFrom).WillReturnRows(hasPriorRows) - - max = sqlmock.NewRows([]string{"max"}).AddRow(4) - open = sqlmock.NewRows([]string{"used"}).AddRow(0) - - custLinks = sqlmock.NewRows([]string{"custodian", "linking_id"}).AddRow("gemini", linkingID.String()) - - // linking limit checks - mock.ExpectQuery("^select wc1.custodian, wc1.linking_id from wallet_custodian (.+)").WithArgs(linkingID).WillReturnRows(custLinks) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID, 4).WillReturnRows(max) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID).WillReturnRows(open) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID).WillReturnRows(sqlmock.NewRows([]string{"wallet_id"}).AddRow(uuid.NewV4().String())) - // get last un linking - lastUnlink = sqlmock.NewRows([]string{"last_unlinking"}).AddRow(time.Now()) - mock.ExpectQuery("^select max(.+)").WithArgs(linkingID).WillReturnRows(lastUnlink) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallet_custodian (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - clRows = sqlmock.NewRows([]string{"created_at", "linked_at"}). - AddRow(time.Now(), time.Now()) - - // insert into wallet custodian - mock.ExpectQuery("^insert into wallet_custodian (.+)").WithArgs(idFrom, "gemini", uuid.NewV5(wallet.ClaimNamespace, accountID.String())).WillReturnRows(clRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idTo, linkingID, "gemini", idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction - mock.ExpectCommit() - - r = r.WithContext(ctx) - - router = chi.NewRouter() - router.Post("/v3/wallet/gemini/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b = rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) -} - -func TestLinkGeminiWalletV3FirstLinking(t *testing.T) { - wallet.VerifiedWalletEnable = true - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - var ( - // setup test variables - idFrom = uuid.NewV4() - ctx = middleware.AddKeyID(context.Background(), idFrom.String()) - accountID = uuid.NewV4() - idTo = accountID - - // setup db mocks - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - linkingInfo = "this is the fake jwt for linking_info" - - // setup mock clients - mockReputationClient = mockreputation.NewMockClient(mockCtrl) - mockGeminiClient = mockgemini.NewMockClient(mockCtrl) - - // this is our main request - r = httptest.NewRequest( - "POST", - fmt.Sprintf("/v3/wallet/gemini/%s/claim", idFrom), - bytes.NewBufferString(fmt.Sprintf(` - { - "linking_info": "%s", - "recipient_id": "%s" - }`, linkingInfo, idTo)), - ) - - mtc = &mockMtc{} - gem = &mockGemini{ - fnGetIssuingCountry: func(acc gemini.ValidatedAccount, fallback bool) string { - return "US" - }, - } - - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, mtc, gem) - handler = wallet.LinkGeminiDepositAccountV3(s) - rw = httptest.NewRecorder() - ) - - mockReputationClient.EXPECT().IsLinkingReputable( - gomock.Any(), // ctx - gomock.Any(), // wallet id - gomock.Any(), // country - ).Return( - true, - []int{}, - nil, - ) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, mockReputationClient) - ctx = context.WithValue(ctx, appctx.GeminiClientCTXKey, mockGeminiClient) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - validateAccountRes := gemini.ValidatedAccount{ - ID: accountID.String(), - ValidDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "US", - }, - }, - } - - mockGeminiClient.EXPECT().FetchValidateAccount( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return(validateAccountRes, nil) - - mockSQLCustodianLink(mock, "gemini") - - // begin linking tx - mock.ExpectBegin() - - // make sure old linking id matches new one for same custodian - linkingID := uuid.NewV5(wallet.ClaimNamespace, idTo.String()) - - // acquire lock for linkingID - mock.ExpectExec("^SELECT pg_advisory_xact_lock\\(hashtext(.+)\\)").WithArgs(linkingID.String()). - WillReturnResult(sqlmock.NewResult(1, 1)) - - // not before linked - mock.ExpectQuery("^select linking_id from (.+)").WithArgs(idFrom, "gemini").WillReturnError(sql.ErrNoRows) - - var max = sqlmock.NewRows([]string{"max"}).AddRow(4) - var open = sqlmock.NewRows([]string{"used"}).AddRow(0) - - var custLinks = sqlmock.NewRows([]string{"custodian", "linking_id"}).AddRow("gemini", linkingID.String()) - - // linking limit checks - mock.ExpectQuery("^select wc1.custodian, wc1.linking_id from wallet_custodian (.+)").WithArgs(linkingID).WillReturnRows(custLinks) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID, 4).WillReturnRows(max) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID).WillReturnRows(open) - mock.ExpectQuery("^select (.+)").WithArgs(linkingID).WillReturnRows(sqlmock.NewRows([]string{"wallet_id"}).AddRow(uuid.NewV4().String())) - // get last un linking - var lastUnlink = sqlmock.NewRows([]string{"last_unlinking"}).AddRow(time.Now()) - mock.ExpectQuery("^select max(.+)").WithArgs(linkingID).WillReturnRows(lastUnlink) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallet_custodian (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - clRows := sqlmock.NewRows([]string{"created_at", "linked_at"}). - AddRow(time.Now(), time.Now()) - - // insert into wallet custodian - mock.ExpectQuery("^insert into wallet_custodian (.+)").WithArgs(idFrom, "gemini", uuid.NewV5(wallet.ClaimNamespace, accountID.String())).WillReturnRows(clRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idTo, linkingID, "gemini", idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectExec("^insert into (.+)").WithArgs(idFrom, true).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction - mock.ExpectCommit() - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Post("/v3/wallet/gemini/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) - - var l wallet.LinkDepositAccountResponse - err := json.Unmarshal(b, &l) - require.NoError(t, err) -} - -func TestLinkZebPayWalletV3_InvalidKyc(t *testing.T) { - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - // setup jwt token for the test - var secret = []byte("a jwt secret") - sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: secret}, (&jose.SignerOptions{}).WithType("JWT")) - if err != nil { - panic(err) - } - - var ( - // setup test variables - idFrom = uuid.NewV4() - ctx = middleware.AddKeyID(context.Background(), idFrom.String()) - accountID = uuid.NewV4() - idTo = accountID - - // setup db mocks - db, _, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - - mtc = &mockMtc{ - fnLinkFailureZP: func(cc string) { - assert.Equal(t, "IN", cc) - }, - } - - gem = &mockGemini{} - - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, mtc, gem) - handler = wallet.LinkZebPayDepositAccountV3(s) - rw = httptest.NewRecorder() - ) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - ctx = context.WithValue(ctx, appctx.ZebPayLinkingKeyCTXKey, base64.StdEncoding.EncodeToString(secret)) - - linkingInfo, err := jwt.Signed(sig).Claims(map[string]interface{}{ - "accountId": accountID, - "depositId": idTo, - "countryCode": "IN", - "iat": time.Now().Unix(), - "exp": time.Now().Add(5 * time.Second).Unix(), - }).CompactSerialize() - if err != nil { - panic(err) - } - - // this is our main request - r := httptest.NewRequest( - "POST", - fmt.Sprintf("/v3/wallet/zebpay/%s/claim", idFrom), - bytes.NewBufferString(fmt.Sprintf( - `{"linking_info": "%s"}`, - linkingInfo, - )), - ) - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Post("/v3/wallet/zebpay/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusForbidden, rw.Code, string(b)) - - var l wallet.LinkDepositAccountResponse - err = json.Unmarshal(b, &l) - require.NoError(t, err) -} - -func TestLinkZebPayWalletV3(t *testing.T) { - wallet.VerifiedWalletEnable = true - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - // setup jwt token for the test - var secret = []byte("a jwt secret") - sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: secret}, (&jose.SignerOptions{}).WithType("JWT")) - if err != nil { - panic(err) - } - - var ( - // setup test variables - idFrom = uuid.NewV4() - ctx = middleware.AddKeyID(context.Background(), idFrom.String()) - accountID = uuid.NewV4() - idTo = accountID - - // setup db mocks - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - - // setup mock clients - mockReputationClient = mockreputation.NewMockClient(mockCtrl) - - mtc = &mockMtc{ - fnLinkSuccessZP: func(cc string) { - assert.Equal(t, "IN", cc) - }, - } - - gem = &mockGemini{} - - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, mtc, gem) - handler = wallet.LinkZebPayDepositAccountV3(s) - rw = httptest.NewRecorder() - ) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, mockReputationClient) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - ctx = context.WithValue(ctx, appctx.ZebPayLinkingKeyCTXKey, base64.StdEncoding.EncodeToString(secret)) - - linkingInfo, err := jwt.Signed(sig).Claims(map[string]interface{}{ - "accountId": accountID, "depositId": idTo, "iat": time.Now().Unix(), "exp": time.Now().Add(5 * time.Second).Unix(), - "isValid": true, "countryCode": "IN", - }).CompactSerialize() - if err != nil { - panic(err) - } - - // this is our main request - r := httptest.NewRequest( - "POST", - fmt.Sprintf("/v3/wallet/zebpay/%s/claim", idFrom), - bytes.NewBufferString(fmt.Sprintf( - `{"linking_info": "%s"}`, - linkingInfo, - )), - ) - - mockReputationClient.EXPECT().IsLinkingReputable( - gomock.Any(), // ctx - gomock.Any(), // wallet id - gomock.Any(), // country - ).Return( - true, - []int{}, - nil, - ) - - mockSQLCustodianLink(mock, "zebpay") - - // begin linking tx - mock.ExpectBegin() - - // make sure old linking id matches new one for same custodian - linkingID := uuid.NewV5(wallet.ClaimNamespace, idTo.String()) - var linkingIDRows = sqlmock.NewRows([]string{"linking_id"}).AddRow(linkingID) - - // acquire lock for linkingID - mock.ExpectExec("^SELECT pg_advisory_xact_lock\\(hashtext(.+)\\)").WithArgs(linkingID.String()). - WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectQuery("^select linking_id from (.+)").WithArgs(idFrom, "zebpay").WillReturnRows(linkingIDRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallet_custodian (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // this wallet has been linked prior, with the same linking id that the request is with - // SHOULD SKIP THE linking limit checks - clRows := sqlmock.NewRows([]string{"created_at", "linked_at"}). - AddRow(time.Now(), time.Now()) - - // insert into wallet custodian - mock.ExpectQuery("^insert into wallet_custodian (.+)").WithArgs(idFrom, "zebpay", uuid.NewV5(wallet.ClaimNamespace, accountID.String())).WillReturnRows(clRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idTo, linkingID, "zebpay", idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectExec("^insert into (.+)").WithArgs(idFrom, true).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction - mock.ExpectCommit() - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Post("/v3/wallet/zebpay/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) - - var l wallet.LinkDepositAccountResponse - err = json.Unmarshal(b, &l) - require.NoError(t, err) - - assert.Equal(t, "IN", l.GeoCountry) -} - -func TestLinkGeminiWalletV3(t *testing.T) { - wallet.VerifiedWalletEnable = true - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - var ( - // setup test variables - idFrom = uuid.NewV4() - ctx = middleware.AddKeyID(context.Background(), idFrom.String()) - accountID = uuid.NewV4() - idTo = accountID - - // setup db mocks - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - linkingInfo = "this is the fake jwt for linking_info" - - // setup mock clients - mockReputationClient = mockreputation.NewMockClient(mockCtrl) - mockGeminiClient = mockgemini.NewMockClient(mockCtrl) - - // this is our main request - r = httptest.NewRequest( - "POST", - fmt.Sprintf("/v3/wallet/gemini/%s/claim", idFrom), - bytes.NewBufferString(fmt.Sprintf(` - { - "linking_info": "%s", - "recipient_id": "%s" - }`, linkingInfo, idTo)), - ) - - mtc = &mockMtc{} - gem = &mockGemini{ - fnGetIssuingCountry: func(acc gemini.ValidatedAccount, fallback bool) string { - return "GB" - }, - } - - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, mtc, gem) - handler = wallet.LinkGeminiDepositAccountV3(s) - rw = httptest.NewRecorder() - ) - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, mockReputationClient) - ctx = context.WithValue(ctx, appctx.GeminiClientCTXKey, mockGeminiClient) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - validateAccountRes := gemini.ValidatedAccount{ - ID: accountID.String(), - ValidDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "GB", - }, - }, - } - - mockGeminiClient.EXPECT().FetchValidateAccount( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return(validateAccountRes, nil) - - mockReputationClient.EXPECT().IsLinkingReputable( - gomock.Any(), // ctx - gomock.Any(), // wallet id - gomock.Any(), // country - ).Return( - true, - []int{}, - nil, - ) - - mockSQLCustodianLink(mock, "gemini") - - // begin linking tx - mock.ExpectBegin() - - // make sure old linking id matches new one for same custodian - linkingID := uuid.NewV5(wallet.ClaimNamespace, idTo.String()) - var linkingIDRows = sqlmock.NewRows([]string{"linking_id"}).AddRow(linkingID) - - // acquire lock for linkingID - mock.ExpectExec("^SELECT pg_advisory_xact_lock\\(hashtext(.+)\\)").WithArgs(linkingID.String()). - WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectQuery("^select linking_id from (.+)").WithArgs(idFrom, "gemini").WillReturnRows(linkingIDRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallet_custodian (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // this wallet has been linked prior, with the same linking id that the request is with - // SHOULD SKIP THE linking limit checks - clRows := sqlmock.NewRows([]string{"created_at", "linked_at"}). - AddRow(time.Now(), time.Now()) - - // insert into wallet custodian - mock.ExpectQuery("^insert into wallet_custodian (.+)").WithArgs(idFrom, "gemini", uuid.NewV5(wallet.ClaimNamespace, accountID.String())).WillReturnRows(clRows) - - // updates the link to the wallet_custodian record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idTo, linkingID, "gemini", idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - mock.ExpectExec("^insert into (.+)").WithArgs(idFrom, true).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction - mock.ExpectCommit() - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Post("/v3/wallet/gemini/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(rw, r) - - b := rw.Body.Bytes() - require.Equal(t, http.StatusOK, rw.Code, string(b)) - - var l wallet.LinkDepositAccountResponse - err := json.Unmarshal(b, &l) - require.NoError(t, err) - - assert.Equal(t, "GB", l.GeoCountry) -} - -func TestDisconnectCustodianLinkV3(t *testing.T) { - wallet.VerifiedWalletEnable = true - - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - var ( - // setup test variables - idFrom = uuid.NewV4() - ctx = middleware.AddKeyID(context.Background(), idFrom.String()) - - // setup db mocks - db, mock, _ = sqlmock.New() - datastore = wallet.Datastore( - &wallet.Postgres{ - Postgres: datastoreutils.Postgres{ - DB: sqlx.NewDb(db, "postgres"), - }, - }) - - // this is our main request - r = httptest.NewRequest( - "DELETE", - fmt.Sprintf("/v3/wallet/gemini/%s/claim", idFrom), nil) - - mtc = &mockMtc{} - gem = &mockGemini{} - - s, _ = wallet.InitService(datastore, nil, nil, nil, nil, nil, mtc, gem) - handler = wallet.DisconnectCustodianLinkV3(s) - w = httptest.NewRecorder() - ) - - // create transaction - mock.ExpectBegin() - - // removes the link to the user_deposit_destination record in wallets - mock.ExpectExec("^update wallets (.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // updates the disconnected date on the record, and returns no error and one changed row - mock.ExpectExec("^update wallet_custodian(.+)").WithArgs(idFrom).WillReturnResult(sqlmock.NewResult(1, 1)) - - // commit transaction because we are done disconnecting - mock.ExpectCommit() - - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, datastore) - ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - r = r.WithContext(ctx) - - router := chi.NewRouter() - router.Delete("/v3/wallet/{custodian}/{paymentID}/claim", handlers.AppHandler(handler).ServeHTTP) - router.ServeHTTP(w, r) - - if resp := w.Result(); resp.StatusCode != http.StatusOK { - must(t, "invalid response", fmt.Errorf("expected %d, got %d", http.StatusOK, resp.StatusCode)) - } -} - -func must(t *testing.T, msg string, err error) { - if err != nil { - t.Errorf("%s: %s\n", msg, err) - } -} - -func signRequest(req *http.Request, publicKey httpsignature.Ed25519PubKey, privateKey ed25519.PrivateKey) error { - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = hex.EncodeToString(publicKey) - s.Headers = []string{"digest", "(request-target)"} - return s.Sign(privateKey, crypto.Hash(0), req) -} - -type result struct{} - -func (r result) LastInsertId() (int64, error) { return 1, nil } -func (r result) RowsAffected() (int64, error) { return 1, nil } - -func mockSQLCustodianLink(mock sqlmock.Sqlmock, custodian string) { - clRow := sqlmock.NewRows([]string{"wallet_id", "custodian", "linking_id", "created_at", "disconnected_at", "linked_at"}). - AddRow(uuid.NewV4().String(), custodian, uuid.NewV4().String(), time.Now(), time.Now(), time.Now()) - mock.ExpectQuery("^select(.+) from wallet_custodian(.+)"). - WillReturnRows(clRow) -} - -type mockGemini struct { - fnGetIssuingCountry func(acc gemini.ValidatedAccount, fallback bool) string - fnIsRegionAllowed func(ctx context.Context, issuingCountry string, custodianRegions custodian.Regions) error -} - -func (m *mockGemini) GetIssuingCountry(acc gemini.ValidatedAccount, fallback bool) string { - if m.fnGetIssuingCountry == nil { - return "" - } - return m.fnGetIssuingCountry(acc, fallback) -} - -func (m *mockGemini) IsRegionAvailable(ctx context.Context, issuingCountry string, custodianRegions custodian.Regions) error { - if m.fnIsRegionAllowed == nil { - return nil - } - return m.fnIsRegionAllowed(ctx, issuingCountry, custodianRegions) -} - -type mockMtc struct { - fnLinkSuccessZP func(cc string) - fnLinkFailureZP func(cc string) -} - -func (m *mockMtc) LinkSuccessZP(cc string) { - if m.fnLinkSuccessZP != nil { - m.fnLinkSuccessZP(cc) - } -} - -func (m *mockMtc) LinkFailureZP(cc string) { - if m.fnLinkFailureZP != nil { - m.fnLinkFailureZP(cc) - } -} - -func (m *mockMtc) LinkFailureGemini(_ string) {} -func (m *mockMtc) LinkSuccessGemini(_ string) {} -func (m *mockMtc) CountDocTypeByIssuingCntry(_ []gemini.ValidDocument) {} diff --git a/services/wallet/controllers_v4.go b/services/wallet/controllers_v4.go deleted file mode 100644 index 8e876cd8f..000000000 --- a/services/wallet/controllers_v4.go +++ /dev/null @@ -1,178 +0,0 @@ -package wallet - -import ( - "crypto" - "encoding/json" - "errors" - "net/http" - - "github.com/asaskevich/govalidator" - "github.com/brave-intl/bat-go/libs/clients" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - "github.com/go-chi/chi" -) - -var ( - errGeoCountryFormat = errors.New("error geo country format must be ISO3166Alpha2") - errGeoAlreadySet = errors.New("error geo country has already been set for rewards wallet") - errPaymentIDMismatch = errors.New("error payment id does not match http signature key id") -) - -// V4Request contains the fields for making v4 wallet requests. -type V4Request struct { - GeoCountry string `json:"geoCountry"` -} - -// V4Response contains the fields for v4 wallet responses. -type V4Response struct { - PaymentID string `json:"paymentId"` -} - -// CreateWalletV4 creates a brave rewards wallet. This endpoint takes a geo country as part of the request -// that must be ISO3166Alpha2 format. -func CreateWalletV4(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - logger := logging.Logger(r.Context(), "wallet.CreateWalletV4") - - verifier := httpsignature.ParameterizedKeystoreVerifier{ - SignatureParams: httpsignature.SignatureParams{ - Algorithm: httpsignature.ED25519, - Headers: []string{"digest", "(request-target)"}, - }, - Keystore: &DecodeEd25519Keystore{}, - Opts: crypto.Hash(0), - } - - ctx, publicKey, err := verifier.VerifyRequest(r) - if err != nil { - logger.Error().Err(err).Msg("error creating rewards wallet") - return handlers.WrapError(err, "error creating rewards wallet", http.StatusUnauthorized) - } - - var request V4Request - err = json.NewDecoder(r.Body).Decode(&request) - if err != nil { - logger.Error().Err(err).Msg("error creating rewards wallet") - return handlers.WrapError(err, "error creating rewards wallet", http.StatusBadRequest) - } - - if !govalidator.IsISO3166Alpha2(request.GeoCountry) { - logger.Error().Err(errGeoCountryFormat).Msg("error creating rewards wallet") - return handlers.WrapError(errGeoCountryFormat, "error creating rewards wallet", http.StatusBadRequest) - } - - info, err := s.CreateRewardsWallet(ctx, publicKey, request.GeoCountry) - if err != nil { - logger.Error().Err(err). - Msg("error creating rewards wallet") - - var errorBundle *errorutils.ErrorBundle - if errors.As(err, &errorBundle) { - logger.Error(). - Str("error_bundle", errorBundle.DataToString()). - Msg("error creating rewards wallet") - } - - switch { - case errors.Is(err, errRewardsWalletAlreadyExists): - return handlers.WrapError(errRewardsWalletAlreadyExists, - "error creating rewards wallet", http.StatusConflict) - case errors.Is(err, errGeoCountryDisabled): - return handlers.WrapError(errGeoCountryDisabled, - "error creating rewards wallet", http.StatusForbidden) - default: - return handlers.WrapError(errorutils.ErrInternalServerError, - "error creating rewards wallet", http.StatusInternalServerError) - } - } - - response := V4Response{ - PaymentID: info.ID, - } - - return handlers.RenderContent(ctx, response, w, http.StatusCreated) - } -} - -// UpdateWalletV4 updates a brave rewards wallet. This endpoint takes a geo country as part of the request that must -// be ISO3166Alpha2 format. -func UpdateWalletV4(s *Service) func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - logger := logging.Logger(r.Context(), "wallet.UpdateWalletV4") - - paymentID := chi.URLParam(r, "paymentID") - if paymentID == "" { - logger.Error().Err(errorutils.ErrBadRequest).Msg("error updating rewards wallet") - return handlers.ValidationError("error validating paymentID url parameter", - map[string]interface{}{"paymentID": errorutils.ErrBadRequest.Error()}) - } - - keyID, err := middleware.GetKeyID(r.Context()) - if err != nil { - logger.Error().Err(err).Msg("error updating rewards wallet") - return handlers.ValidationError("error retrieving keyID from signature", - map[string]interface{}{"keyID": err.Error()}) - } - - if paymentID != keyID { - logger.Error().Err(errPaymentIDMismatch).Msg("error updating rewards wallet") - return handlers.WrapError(errPaymentIDMismatch, "error updating rewards wallet", http.StatusForbidden) - } - - var request V4Request - err = json.NewDecoder(r.Body).Decode(&request) - if err != nil { - logger.Error().Err(err).Msg("error updating rewards wallet") - return handlers.WrapError(err, "error updating rewards wallet", http.StatusBadRequest) - } - - if !govalidator.IsISO3166Alpha2(request.GeoCountry) { - logger.Error().Err(errGeoCountryFormat).Msg("error updating rewards wallet") - return handlers.WrapError(errGeoCountryFormat, "error updating rewards wallet", http.StatusBadRequest) - } - - // Currently we do not check for the wallet existence as the middleware LookupVerifier covers this. - upsertReputationSummary := func() (interface{}, error) { - return nil, s.repClient.UpsertReputationSummary(r.Context(), paymentID, request.GeoCountry) - } - - _, err = s.retry(r.Context(), upsertReputationSummary, retryPolicy, canRetry(nonRetriableErrors)) - if err != nil { - logger.Error().Err(err).Msg("error updating rewards wallet") - var errorBundle *errorutils.ErrorBundle - if errors.As(err, &errorBundle) { - logger.Error(). - Str("error bundle", errorBundle.DataToString()). - Msg("error updating rewards wallet") - if httpState, ok := errorBundle.Data().(clients.HTTPState); ok { - if httpState.Status == http.StatusBadRequest { - return handlers.WrapError(errorutils.ErrBadRequest, - "error updating rewards wallet", http.StatusBadRequest) - } - if httpState.Status == http.StatusConflict { - return handlers.WrapError(errGeoAlreadySet, - "error updating rewards wallet", http.StatusConflict) - } - } - } - return handlers.WrapError(errorutils.ErrInternalServerError, "error updating rewards wallet", - http.StatusInternalServerError) - } - - return handlers.RenderContent(r.Context(), nil, w, http.StatusOK) - } -} - -// GetWalletV4 is the same as get wallet v3, but we are now requiring http signatures for get wallet requests -func GetWalletV4(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return GetWalletV3(w, r) -} - -// GetUpholdWalletBalanceV4 produces an http handler for the service s which handles balance inquiries of uphold wallets -func GetUpholdWalletBalanceV4(w http.ResponseWriter, r *http.Request) *handlers.AppError { - return GetUpholdWalletBalanceV3(w, r) -} diff --git a/services/wallet/controllers_v4_test.go b/services/wallet/controllers_v4_test.go deleted file mode 100644 index cd0b0dcdd..000000000 --- a/services/wallet/controllers_v4_test.go +++ /dev/null @@ -1,566 +0,0 @@ -//go:build integration - -package wallet_test - -import ( - "bytes" - "context" - "crypto" - "crypto/ed25519" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - errorutils "github.com/brave-intl/bat-go/libs/errors" - - "github.com/brave-intl/bat-go/libs/clients" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/backoff" - mockreputation "github.com/brave-intl/bat-go/libs/clients/reputation/mock" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/test" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/services/wallet" - "github.com/brave-intl/bat-go/services/wallet/wallettest" - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/suite" -) - -type WalletControllersV4TestSuite struct { - storage wallet.Datastore - suite.Suite -} - -func TestWalletControllersV4TestSuite(t *testing.T) { - suite.Run(t, new(WalletControllersV4TestSuite)) -} - -func (suite *WalletControllersV4TestSuite) SetupSuite() { - wallettest.Migrate(suite.T()) - storage, _ := wallet.NewWritablePostgres("", false, "") - suite.storage = storage -} - -func (suite *WalletControllersV4TestSuite) SetupTest() { - wallettest.CleanDB(suite.T(), suite.storage.RawDB()) -} - -func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_Success() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - reputationClient := mockreputation.NewMockClient(ctrl) - reputationClient.EXPECT(). - UpsertReputationSummary(gomock.Any(), gomock.Any(), gomock.Any()). - Return(nil) - - geoCountry := "AF" - - locationValidator := wallet.NewMockGeoValidator(ctrl) - locationValidator.EXPECT(). - Validate(gomock.Any(), geoCountry). - Return(true, nil) - - service, err := wallet.InitService(storage, nil, reputationClient, nil, locationValidator, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: geoCountry, - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - err = signRequest(request, publicKey, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusCreated, rw.Code) - - var response wallet.V4Response - err = json.NewDecoder(rw.Body).Decode(&response) - suite.Require().NoError(err) - - walletID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()) - suite.Assert().Equal(walletID.String(), response.PaymentID) -} - -func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_GeoCountryDisabled() { - ctx := context.Background() - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - locationValidator := wallet.NewMockGeoValidator(ctrl) - locationValidator.EXPECT(). - Validate(gomock.Any(), gomock.Any()). - Return(false, nil) - - service, err := wallet.InitService(nil, nil, nil, nil, locationValidator, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - err = signRequest(request, publicKey, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Assert().Equal(http.StatusForbidden, rw.Code) - - walletID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()) - - info, err := suite.storage.GetWallet(ctx, walletID) - suite.Require().NoError(err) - - suite.Assert().Nil(info) -} - -func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_WalletAlreadyExists() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - geoCountry := "AF" - - locationValidator := wallet.NewMockGeoValidator(ctrl) - locationValidator.EXPECT(). - Validate(gomock.Any(), geoCountry). - Return(true, nil) - - service, err := wallet.InitService(storage, nil, nil, nil, locationValidator, nil, nil, nil) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: geoCountry, - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - err = signRequest(request, publicKey, privateKey) - suite.Require().NoError(err) - - // create existing wallet - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() - var altCurrency = altcurrency.BAT - info := &walletutils.Info{ - ID: paymentID, - Provider: "brave", - PublicKey: publicKey.String(), - AltCurrency: &altCurrency, - } - - err = suite.storage.InsertWallet(ctx, info) - suite.Require().NoError(err) - - // execute request - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusConflict, rw.Code) - - var appError handlers.AppError - err = json.NewDecoder(rw.Body).Decode(&appError) - suite.Require().NoError(err) - - suite.Assert().Contains(appError.Message, "rewards wallet already exists") -} - -func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_ReputationCallFailed() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - errReputation := errorutils.New(errors.New(test.RandomString()), - test.RandomString(), test.RandomString()) - - reputationClient := mockreputation.NewMockClient(ctrl) - reputationClient.EXPECT(). - UpsertReputationSummary(gomock.Any(), gomock.Any(), gomock.Any()). - Return(errReputation) - - locationValidator := wallet.NewMockGeoValidator(ctrl) - locationValidator.EXPECT(). - Validate(gomock.Any(), gomock.Any()). - Return(true, nil) - - service, err := wallet.InitService(storage, nil, reputationClient, nil, locationValidator, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - err = signRequest(request, publicKey, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Assert().Equal(http.StatusInternalServerError, rw.Code) - - walletID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()) - - info, err := suite.storage.GetWallet(ctx, walletID) - suite.Require().NoError(err) - - suite.Assert().Nil(info) -} - -func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_Success() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - reputationClient := mockreputation.NewMockClient(ctrl) - reputationClient.EXPECT(). - UpsertReputationSummary(gomock.Any(), gomock.Any(), gomock.Any()). - Return(nil) - - service, err := wallet.InitService(storage, nil, reputationClient, nil, nil, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() - - var altCurrency = altcurrency.BAT - info := &walletutils.Info{ - ID: paymentID, - Provider: "brave", - PublicKey: publicKey.String(), - AltCurrency: &altCurrency, - } - - err = suite.storage.InsertWallet(ctx, info) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), - bytes.NewBuffer(payload)) - - err = signUpdateRequest(request, paymentID, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusOK, rw.Code) -} - -func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_VerificationMissingWallet() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - service, err := wallet.InitService(storage, nil, nil, nil, nil, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), - bytes.NewBuffer(payload)) - - err = signUpdateRequest(request, paymentID, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusForbidden, rw.Code) -} - -func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_PaymentIDMismatch() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - service, err := wallet.InitService(storage, nil, nil, nil, nil, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() - - var altCurrency = altcurrency.BAT - info := &walletutils.Info{ - ID: paymentID, - Provider: "brave", - PublicKey: publicKey.String(), - AltCurrency: &altCurrency, - } - - err = suite.storage.InsertWallet(ctx, info) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", uuid.NewV4()), - bytes.NewBuffer(payload)) - - err = signUpdateRequest(request, paymentID, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusForbidden, rw.Code) - - var appError handlers.AppError - err = json.NewDecoder(rw.Body).Decode(&appError) - suite.Require().NoError(err) - - suite.Assert().Contains(appError.Message, "error payment id does not match http signature key id") -} - -func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_GeoCountryAlreadySet() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - errorBundle := clients.NewHTTPError(errors.New(test.RandomString()), test.RandomString(), - test.RandomString(), http.StatusConflict, nil) - - reputationClient := mockreputation.NewMockClient(ctrl) - reputationClient.EXPECT(). - UpsertReputationSummary(gomock.Any(), gomock.Any(), gomock.Any()). - Return(errorBundle) - - service, err := wallet.InitService(storage, nil, reputationClient, nil, nil, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() - - var altCurrency = altcurrency.BAT - info := &walletutils.Info{ - ID: paymentID, - Provider: "brave", - PublicKey: publicKey.String(), - AltCurrency: &altCurrency, - } - - err = suite.storage.InsertWallet(ctx, info) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), - bytes.NewBuffer(payload)) - - err = signUpdateRequest(request, paymentID, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusConflict, rw.Code) - - var appError handlers.AppError - err = json.NewDecoder(rw.Body).Decode(&appError) - suite.Require().NoError(err) - - suite.Assert().Contains(appError.Error(), "error geo country has already been set for rewards wallet") -} - -func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_ReputationCallFailed() { - ctx := context.Background() - - storage, err := wallet.NewWritablePostgres("", false, "") - suite.NoError(err) - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - errReputation := errors.New(test.RandomString()) - reputationClient := mockreputation.NewMockClient(ctrl) - reputationClient.EXPECT(). - UpsertReputationSummary(gomock.Any(), gomock.Any(), gomock.Any()). - Return(errReputation) - - service, err := wallet.InitService(storage, nil, reputationClient, nil, nil, backoff.Retry, nil, nil) - suite.Require().NoError(err) - - // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - suite.Require().NoError(err) - - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() - - var altCurrency = altcurrency.BAT - info := &walletutils.Info{ - ID: paymentID, - Provider: "brave", - PublicKey: publicKey.String(), - AltCurrency: &altCurrency, - } - - err = suite.storage.InsertWallet(ctx, info) - suite.Require().NoError(err) - - router := chi.NewRouter() - wallet.RegisterRoutes(ctx, service, router) - - data := wallet.V4Request{ - GeoCountry: "AF", - } - - payload, err := json.Marshal(data) - suite.Require().NoError(err) - - rw := httptest.NewRecorder() - - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), - bytes.NewBuffer(payload)) - - err = signUpdateRequest(request, paymentID, privateKey) - suite.Require().NoError(err) - - server := &http.Server{Addr: ":8080", Handler: router} - server.Handler.ServeHTTP(rw, request) - - suite.Require().Equal(http.StatusInternalServerError, rw.Code) -} - -func signUpdateRequest(req *http.Request, paymentID string, privateKey ed25519.PrivateKey) error { - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = paymentID - s.Headers = []string{"digest", "(request-target)"} - return s.Sign(privateKey, crypto.Hash(0), req) -} diff --git a/services/wallet/datastore.go b/services/wallet/datastore.go deleted file mode 100644 index 071897a82..000000000 --- a/services/wallet/datastore.go +++ /dev/null @@ -1,1045 +0,0 @@ -package wallet - -import ( - "context" - "database/sql" - "errors" - "fmt" - "net/http" - "os" - "strconv" - "time" - - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/services/wallet/model" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients/reputation" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/logging" - timeutils "github.com/brave-intl/bat-go/libs/time" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/getsentry/sentry-go" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - uuid "github.com/satori/go.uuid" - - // needed for magic migration - _ "github.com/golang-migrate/migrate/v4/source/file" -) - -var ( - metricTxLockGauge = prometheus.NewGauge( - prometheus.GaugeOpts{ - Name: "pg_tx_advisory_lock_gauge", - Help: "Monitors number of tx advisory locks", - ConstLabels: prometheus.Labels{"service": "wallet"}, - }) - tooManyCardsCounter = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "too_many_linked_cards", - Help: "A counter for too many linked cards", - ConstLabels: prometheus.Labels{"service": "wallet"}, - }) - tenLinkagesReached = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "custodian_account_linked_ten_times", - Help: "A counter for seeing how many custodian accounts have been linked 10 times", - ConstLabels: prometheus.Labels{"service": "wallet"}, - }) -) - -func init() { - prometheus.MustRegister(tooManyCardsCounter) - prometheus.MustRegister(metricTxLockGauge) - prometheus.MustRegister(tenLinkagesReached) -} - -// Datastore holds the interface for the wallet datastore -type Datastore interface { - datastore.Datastore - LinkWallet(ctx context.Context, id string, providerID string, providerLinkingID uuid.UUID, depositProvider, country string) error - GetLinkingLimitInfo(ctx context.Context, providerLinkingID string) (map[string]LinkingInfo, error) - HasPriorLinking(ctx context.Context, walletID uuid.UUID, providerLinkingID uuid.UUID) (bool, error) - // GetLinkingsByProviderLinkingID gets the wallet linking info by provider linking id - GetLinkingsByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) ([]LinkingMetadata, error) - // GetByProviderLinkingID gets the wallet by provider linking id - GetByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (*[]walletutils.Info, error) - // GetWallet by ID - GetWallet(ctx context.Context, ID uuid.UUID) (*walletutils.Info, error) - // GetWalletByPublicKey by ID - GetWalletByPublicKey(context.Context, string) (*walletutils.Info, error) - // InsertWallet inserts the given wallet - InsertWallet(ctx context.Context, wallet *walletutils.Info) error - // InsertWalletTx inserts the given wallet as part of provided sql.Tx transaction. - InsertWalletTx(ctx context.Context, tx *sqlx.Tx, wallet *walletutils.Info) error - // InsertBitFlyerRequestID - attempt an insert on a request id - InsertBitFlyerRequestID(ctx context.Context, requestID string) error - // UpsertWallet UpsertWallets inserts a wallet if it does not already exist - UpsertWallet(ctx context.Context, wallet *walletutils.Info) error - // ConnectCustodialWallet - connect the wallet's custodial verified wallet. - ConnectCustodialWallet(ctx context.Context, cl *CustodianLink, depositDest string) error - // DisconnectCustodialWallet - disconnect the wallet's custodial id - DisconnectCustodialWallet(ctx context.Context, walletID uuid.UUID) error - // GetCustodianLinkByWalletID retrieves the currently linked wallet custodian by walletID. - GetCustodianLinkByWalletID(ctx context.Context, ID uuid.UUID) (*CustodianLink, error) - // GetCustodianLinkCount - get the wallet custodian link count across all wallets - GetCustodianLinkCount(ctx context.Context, linkingID uuid.UUID, custodian string) (int, int, error) - // InsertVerifiedWalletOutboxTx inserts a verifiedWalletOutbox for processing. - InsertVerifiedWalletOutboxTx(ctx context.Context, tx *sqlx.Tx, paymentID uuid.UUID, verifiedWallet bool) error - // SendVerifiedWalletOutbox sends requests to reputation service. - SendVerifiedWalletOutbox(ctx context.Context, client reputation.Client, retry backoff.RetryFunc) (bool, error) -} - -// ReadOnlyDatastore includes all database methods that can be made with a read only db connection -type ReadOnlyDatastore interface { - datastore.Datastore - HasPriorLinking(ctx context.Context, walletID uuid.UUID, providerLinkingID uuid.UUID) (bool, error) - // GetLinkingsByProviderLinkingID gets the wallet linking info by provider linking id - GetLinkingsByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) ([]LinkingMetadata, error) - // GetByProviderLinkingID gets a wallet by provider linking id - GetByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (*[]walletutils.Info, error) - // GetWallet by ID - GetWallet(ctx context.Context, ID uuid.UUID) (*walletutils.Info, error) - // GetWalletByPublicKey retrieves a wallet by its public key. - GetWalletByPublicKey(context.Context, string) (*walletutils.Info, error) - // GetCustodianLinkCount - get the wallet custodian link count across all wallets - GetCustodianLinkCount(ctx context.Context, linkingID uuid.UUID, custodian string) (int, int, error) -} - -// Postgres is a Datastore wrapper around a postgres database -type Postgres struct { - datastore.Postgres -} - -// NewWritablePostgres creates a new Postgres Datastore -func NewWritablePostgres(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (Datastore, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if pg != nil { - return &DatastoreWithPrometheus{ - base: &Postgres{*pg}, instanceName: "wallet_datastore", - }, err - } - return nil, err -} - -// NewReadOnlyPostgres creates a new Postgres RO Datastore -func NewReadOnlyPostgres(databaseURL string, performMigration bool, migrationTrack string, dbStatsPrefix ...string) (ReadOnlyDatastore, error) { - pg, err := datastore.NewPostgres(databaseURL, performMigration, migrationTrack, dbStatsPrefix...) - if pg != nil { - return &ReadOnlyDatastoreWithPrometheus{ - base: &Postgres{*pg}, instanceName: "wallet_ro_datastore", - }, err - } - return nil, err -} - -// NewPostgres creates postgres connections -func NewPostgres() (Datastore, ReadOnlyDatastore, error) { - var walletRODB ReadOnlyDatastore - walletDB, err := NewWritablePostgres("", true, "wallet") - if err != nil { - sentry.CaptureException(err) - log.Panic().Err(err).Msg("Must be able to init postgres connection to start") - return nil, nil, err - } - roDB := os.Getenv("RO_DATABASE_URL") - if len(roDB) > 0 { - walletRODB, err = NewReadOnlyPostgres(roDB, false, "wallet") - if err != nil { - sentry.CaptureException(err) - log.Error().Err(err).Msg("Could not start reader postgres connection") - return nil, nil, err - } - } - return walletDB, walletRODB, nil -} - -var ( - // ErrTooManyCardsLinked denotes when more than 3 cards have been linked to a single wallet - ErrTooManyCardsLinked = errors.New("unable to add too many wallets to a single user") - // ErrNoReputationClient is returned when no reputation client is in the ctx. - ErrNoReputationClient = errors.New("wallet: no reputation client") -) - -// UpsertWallet upserts the given wallet -func (pg *Postgres) UpsertWallet(ctx context.Context, wallet *walletutils.Info) error { - statement := ` - insert into wallets - ( - id, provider, provider_id, public_key, provider_linking_id, anonymous_address, - user_deposit_account_provider, user_deposit_destination - ) - values - ($1, $2, $3, $4, $5, $6, $7, $8) - on conflict (id) do - update set - provider = $2, - provider_id = $3, - provider_linking_id = $5, - anonymous_address = $6, - user_deposit_account_provider = $7, - user_deposit_destination = $8 - returning *` - _, err := pg.RawDB().ExecContext(ctx, statement, wallet.ID, wallet.Provider, wallet.ProviderID, wallet.PublicKey, wallet.ProviderLinkingID, wallet.AnonymousAddress, wallet.UserDepositAccountProvider, wallet.UserDepositDestination) - if err != nil { - return err - } - - return nil -} - -// GetWallet by ID -func (pg *Postgres) GetWallet(ctx context.Context, ID uuid.UUID) (*walletutils.Info, error) { - statement := ` - select - id, provider, provider_id, public_key, provider_linking_id, anonymous_address, - user_deposit_account_provider, user_deposit_destination - from - wallets - where - id = $1` - wallets := []walletutils.Info{} - err := pg.RawDB().SelectContext(ctx, &wallets, statement, ID) - if err != nil { - return nil, err - } - - if len(wallets) > 0 { - // FIXME currently assumes BAT - { - tmp := altcurrency.BAT - wallets[0].AltCurrency = &tmp - } - return &wallets[0], nil - } - - return nil, nil -} - -// GetWalletByPublicKey gets a wallet by a public key -func (pg *Postgres) GetWalletByPublicKey(ctx context.Context, pk string) (*walletutils.Info, error) { - statement := ` - select - id, provider, provider_id, public_key, provider_linking_id, anonymous_address, - user_deposit_account_provider, user_deposit_destination - from - wallets - WHERE public_key = $1 - ` - var wallet walletutils.Info - err := pg.RawDB().GetContext(ctx, &wallet, statement, pk) - return &wallet, err -} - -// HasPriorLinking - check if this wallet id has been linked to this provider linking id in the past -func (pg *Postgres) HasPriorLinking(ctx context.Context, walletID uuid.UUID, providerLinkingID uuid.UUID) (bool, error) { - statement := ` - select exists ( - select 1 - from - wallet_custodian - where - linking_id = $1 and wallet_id = $2 - ) - ` - var existingLinking bool - err := pg.RawDB().GetContext(ctx, &existingLinking, statement, providerLinkingID, walletID) - return existingLinking, err -} - -// GetLinkingsByProviderLinkingID gets wallet linkings by a provider address -func (pg *Postgres) GetLinkingsByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) ([]LinkingMetadata, error) { - statement := ` - select - wallet_id, disconnected_at, created_at, linked_at, unlinked_at, - (disconnected_at is null and unlinked_at is null) as active - from - wallet_custodian - WHERE linking_id = $1 - ` - var linkings []LinkingMetadata - err := pg.RawDB().SelectContext(ctx, &linkings, statement, providerLinkingID) - return linkings, err -} - -// GetByProviderLinkingID gets a wallet by a provider address -func (pg *Postgres) GetByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (*[]walletutils.Info, error) { - statement := ` - select - id, provider, provider_id, public_key, provider_linking_id, anonymous_address, - user_deposit_account_provider, user_deposit_destination - from - wallets - WHERE provider_linking_id = $1 - ` - var wallets []walletutils.Info - err := pg.RawDB().SelectContext(ctx, &wallets, statement, providerLinkingID) - return &wallets, err -} - -// InsertBitFlyerRequestID - attempts to insert a request id -func (pg *Postgres) InsertBitFlyerRequestID(ctx context.Context, requestID string) error { - statement := `insert into bf_req_ids(id) values ($1)` - _, err := pg.RawDB().ExecContext(ctx, statement, requestID) - if err != nil { - return err - } - - return nil - -} - -// InsertWallet inserts the given wallet -func (pg *Postgres) InsertWallet(ctx context.Context, wallet *walletutils.Info) error { - // NOTE on conflict do nothing because none of the wallet information is updateable - statement := ` - INSERT INTO wallets (id, provider, provider_id, public_key) - VALUES ($1, $2, $3, $4) - ON CONFLICT DO NOTHING` - _, err := pg.RawDB().ExecContext(ctx, - statement, - wallet.ID, - wallet.Provider, - wallet.ProviderID, - wallet.PublicKey, - ) - if err != nil { - return err - } - - return nil -} - -// InsertWalletTx inserts the given wallet -func (pg *Postgres) InsertWalletTx(ctx context.Context, tx *sqlx.Tx, wallet *walletutils.Info) error { - statement := `INSERT INTO wallets (id, provider, provider_id, public_key) VALUES ($1, $2, $3, $4)` - _, err := tx.ExecContext(ctx, - statement, - wallet.ID, - wallet.Provider, - wallet.ProviderID, - wallet.PublicKey, - ) - if err != nil { - return err - } - return nil -} - -type custodianLinkingID struct { - Custodian string `db:"custodian"` - LinkingID *uuid.UUID `db:"linking_id"` -} - -func txGetCustodianLinkingIDs(ctx context.Context, tx *sqlx.Tx, providerLinkingID string) (map[string]string, error) { - var ( - custodianLinkingIDs = []custodianLinkingID{} - resp = map[string]string{} - ) - statement := ` - select wc1.custodian, wc1.linking_id from wallet_custodian wc1 join wallet_custodian wc2 on - (wc1.wallet_id=wc2.wallet_id) where wc2.linking_id = $1 and wc1.unlinked_at is null and wc2.unlinked_at is null - ` - err := tx.Select(&custodianLinkingIDs, statement, providerLinkingID) - if err != nil { - return nil, fmt.Errorf("failed to associate linking id to custodians: %w", err) - } - - for _, v := range custodianLinkingIDs { - resp[v.Custodian] = v.LinkingID.String() - } - - return resp, nil -} - -func txGetMaxLinkingSlots(ctx context.Context, tx *sqlx.Tx, custodian, providerLinkingID string) (int, error) { - var ( - max int - ) - statement := ` - select ($2 + count(1)) as max from linking_limit_adjust where provider_linking_id = $1 - ` - err := tx.Get(&max, statement, providerLinkingID, getEnvMaxCards(custodian)) - return max, err -} - -func txGetUsedLinkingSlots(ctx context.Context, tx *sqlx.Tx, providerLinkingID string) (int, error) { - var ( - used int - ) - // we need to exclude `this` wallet from the used computation in the event we are attempting - // to re-link the 4th slot - statement := ` - select count(distinct(wallet_id)) as used from wallet_custodian where linking_id = $1 and unlinked_at is null - ` - err := tx.Get(&used, statement, providerLinkingID) - return used, err -} - -func bitFlyerRequestIDSpent(ctx context.Context, requestID string) bool { - logger := logging.Logger(ctx, "wallet.bitFlyerRequestIDSpent") - // get pg from context - db, ok := ctx.Value(appctx.DatastoreCTXKey).(Datastore) - if !ok { - // if we cant check the db consider "spent" - logger.Error().Msg("bitFlyerRequestIDSpent: unable to get datastore from context") - return true - } - - // attempt an insert into the spent bf request id table - // if duplicate error, false - if err := db.InsertBitFlyerRequestID(ctx, requestID); err != nil { - // check error, consider "spent" if error - logger.Error().Err(err).Msg("bitFlyerRequestIDSpent: database error attempting to insert") - return true - } - // else not spent if successfully inserted - return false -} - -func getEnvMaxCards(custodian string) int { - switch custodian { - case "uphold": - if v, err := strconv.Atoi(os.Getenv("UPHOLD_WALLET_LINKING_LIMIT")); err == nil { - return v - } - case "bitflyer": - if v, err := strconv.Atoi(os.Getenv("BITFLYER_WALLET_LINKING_LIMIT")); err == nil { - return v - } - case "gemini": - if v, err := strconv.Atoi(os.Getenv("GEMINI_WALLET_LINKING_LIMIT")); err == nil { - return v - } - case "zebpay": - if v, err := strconv.Atoi(os.Getenv("ZEBPAY_WALLET_LINKING_LIMIT")); err == nil { - return v - } - } - return 4 -} - -// LinkingMetadata - show more details in linking info about the linkages -type LinkingMetadata struct { - WalletID uuid.UUID `json:"id" db:"wallet_id"` - DisconnectedAt *time.Time `json:"disconnectedAt,omitempty" db:"disconnected_at"` - LastLinkedAt *time.Time `json:"lastLinkedAt,omitempty" db:"linked_at"` - FirstLinkedAt *time.Time `json:"firstLinkedAt,omitempty" db:"created_at"` - UnLinkedAt *time.Time `json:"unlinkedAt,omitempty" db:"unlinked_at"` - Active bool `json:"active" db:"active"` -} - -// LinkingInfo - a structure for wallet linking information -type LinkingInfo struct { - LinkingID *uuid.UUID `json:"-"` - NextAvailableUnlinking *time.Time `json:"nextAvailableUnlinking,omitempty"` - WalletsLinked int `json:"walletsLinked"` - OpenLinkingSlots int `json:"openLinkingSlots"` - OtherWalletsLinked []LinkingMetadata `json:"otherWalletsLinked,omitempty"` -} - -// GetLinkingLimitInfo - get some basic info about linking limit -func (pg *Postgres) GetLinkingLimitInfo(ctx context.Context, providerLinkingID string) (map[string]LinkingInfo, error) { - var infos = map[string]LinkingInfo{} - - // get tx - _, tx, rollback, commit, err := getTx(ctx, pg) - if err != nil { - return nil, fmt.Errorf("failed to create db transaction GetLinkingLimitInfo: %w", err) - } - // will rollback if tx created at this scope - defer rollback() - - // find all custodians that have been linked to this wallet based on providerLinkingID - custodianLinkingIDs, err := txGetCustodianLinkingIDs(ctx, tx, providerLinkingID) - if err != nil { - return nil, fmt.Errorf("failed to get custodian linking ids: %w", err) - } - - // for each custodian linking id found, get the max/used - for custodian, linkingID := range custodianLinkingIDs { - maxLinkings, err := txGetMaxLinkingSlots(ctx, tx, custodian, linkingID) - if err != nil { - return nil, errorutils.Wrap(err, "error looking up max linkings for wallet") - } - - usedLinkings, err := txGetUsedLinkingSlots(ctx, tx, linkingID) - if err != nil { - return nil, errorutils.Wrap(err, "error looking up used linkings for wallet") - } - - // convert linking id to uuid - lID, err := uuid.FromString(linkingID) - if err != nil { - return nil, fmt.Errorf("failed to parse linking id: %w", err) - } - - // lookup other linked wallets - linkings, err := pg.GetLinkingsByProviderLinkingID(ctx, lID) - if err != nil { - return nil, fmt.Errorf("failed to get other wallets by linking id: %w", err) - } - - // get the next available unlinking time - // lower bound based - lbDur, ok := ctx.Value(appctx.NoUnlinkPriorToDurationCTXKey).(string) - if !ok { - return nil, fmt.Errorf("misconfigured service, no unlink prior to duration configured") - } - - d, err := timeutils.ParseDuration(lbDur) - if err != nil { - return nil, fmt.Errorf("misconfigured service, invalid no unlink prior to duration configured") - } - - // get the latest unlinking - stmt := ` - select - max(unlinked_at) as last_unlinking - from - wallet_custodian - where - linking_id = $1 and unlinked_at is not null - ` - var last *time.Time - err = tx.Get(&last, stmt, lID) - if err != nil && err != sql.ErrNoRows { - return nil, fmt.Errorf("failed to get max time: %w", err) - } - - var nextUnlink time.Time - - if last != nil { - notBefore, err := d.FromNow() - if err != nil { - return nil, fmt.Errorf("unable to get not before time from duration: %w", err) - } - if (*notBefore).Before(*last) { - // last - notBefore is the duration we need to cool off - // now + ( last - notBefore ) - nextUnlink = time.Now().Add(last.Sub(*notBefore)) - } - } - - // add to result - infos[custodian] = LinkingInfo{ - NextAvailableUnlinking: &nextUnlink, - LinkingID: &lID, - WalletsLinked: usedLinkings, - OpenLinkingSlots: maxLinkings - usedLinkings, - OtherWalletsLinked: linkings, - } - } - - // if the tx was created in this scope we will commit here - if err := commit(); err != nil { - return nil, fmt.Errorf("failed to commit GetLinkingLimitInfo transaction: %w", err) - } - - return infos, nil -} - -var ( - // ErrUnusualActivity - error for wallets with unusual activity - ErrUnusualActivity = errors.New("unusual activity") - // ErrGeoResetDifferent - error for wallets with reset geo - ErrGeoResetDifferent = errors.New("geo reset is different") -) - -// LinkWallet links a rewards wallet to the given deposit provider. -func (pg *Postgres) LinkWallet(ctx context.Context, id string, userDepositDestination string, providerLinkingID uuid.UUID, depositProvider, country string) error { - walletID, err := uuid.FromString(id) - if err != nil { - return fmt.Errorf("invalid wallet id, not uuid: %w", err) - } - - repClient, ok := ctx.Value(appctx.ReputationClientCTXKey).(reputation.Client) - if !ok { - return ErrNoReputationClient - } - - // TODO(clD11): We no longer need to act on the response and only require a successful call to reputation to - // continue linking. As part of the wallet refactor we should clean this up. - if _, _, err := repClient.IsLinkingReputable(ctx, walletID, country); err != nil { - return fmt.Errorf("failed to check wallet rep: %w", err) - } - - ctx, tx, rollback, commit, err := getTx(ctx, pg) - if err != nil { - return fmt.Errorf("error getting tx: %w", err) - } - defer func() { - metricTxLockGauge.Dec() - rollback() - }() - - metricTxLockGauge.Inc() - if err := waitAndLockTx(ctx, tx, providerLinkingID); err != nil { - return fmt.Errorf("error acquiring tx lock: %w", err) - } - - if err := pg.ConnectCustodialWallet(ctx, &CustodianLink{ - WalletID: &walletID, - Custodian: depositProvider, - LinkingID: &providerLinkingID, - }, userDepositDestination); err != nil { - return fmt.Errorf("error connect custodian wallet: %w", err) - } - - // TODO(clD11): the below verified wallets calls were added as a quick fix and should be addressed in the wallet refactor. - if VerifiedWalletEnable { - if err := pg.InsertVerifiedWalletOutboxTx(ctx, tx, walletID, true); err != nil { - return fmt.Errorf("failed to update verified wallet: %w", err) - } - } - - if directVerifiedWalletEnable { - op := func() (interface{}, error) { - return nil, repClient.UpdateReputationSummary(ctx, walletID.String(), true) - } - if _, err := backoff.Retry(ctx, op, retryPolicy, canRetry(nonRetriableErrors)); err != nil { - return fmt.Errorf("failed to update verified wallet: %w", err) - } - } - - if err := commit(); err != nil { - sentry.CaptureException(fmt.Errorf("error failed to commit link wallet transaction: %w", err)) - return fmt.Errorf("error committing tx: %w", err) - } - - return nil -} - -// CustodianLink - representation of wallet_custodian record -type CustodianLink struct { - WalletID *uuid.UUID `json:"wallet_id" db:"wallet_id" valid:"uuidv4"` - Custodian string `json:"custodian" db:"custodian" valid:"in(uphold,brave,gemini,bitflyer)"` - CreatedAt time.Time `json:"created_at" db:"created_at" valid:"-"` - UpdatedAt *time.Time `json:"updated_at" db:"updated_at" valid:"-"` - LinkedAt time.Time `json:"linked_at" db:"linked_at" valid:"-"` - DisconnectedAt *time.Time `json:"disconnected_at" db:"disconnected_at" valid:"-"` - DepositDestination string `json:"deposit_destination" db:"deposit_destination" valid:"-"` - LinkingID *uuid.UUID `json:"linking_id" db:"linking_id" valid:"uuid"` - UnlinkedAt *time.Time `json:"unlinked_at" db:"unlinked_at" valid:"-"` -} - -// GetWalletIDString - get string version of the WalletID -func (cl *CustodianLink) GetWalletIDString() string { - if cl.WalletID != nil { - return cl.WalletID.String() - } - return "" -} - -// GetLinkingIDString - get string version of the LinkingID -func (cl *CustodianLink) GetLinkingIDString() string { - if cl.LinkingID != nil { - return cl.LinkingID.String() - } - return "" -} - -func (cl *CustodianLink) isLinked() bool { - return cl != nil && cl.UnlinkedAt == nil && cl.DisconnectedAt == nil && !cl.LinkedAt.IsZero() -} - -// GetCustodianLinkCount - get the wallet custodian link count across all wallets -func (pg *Postgres) GetCustodianLinkCount(ctx context.Context, linkingID uuid.UUID, custodian string) (int, int, error) { - // the count of linked wallets - var err error - - // create a sublogger - sublogger := logger(ctx).With(). - Str("linking_id", linkingID.String()). - Logger() - - sublogger.Debug(). - Msg("starting GetCustodianLinkCount") - - // get tx - ctx, _, rollback, commit, err := getTx(ctx, pg) - if err != nil { - return 0, 0, fmt.Errorf("failed to create db transaction GetCustodianLinkByWalletID: %w", err) - } - // will rollback if tx created at this scope - defer rollback() - - li, err := pg.GetLinkingLimitInfo(ctx, linkingID.String()) - if err != nil { - sublogger.Error().Err(err). - Msg("failed to get CustodianLinkCount from DB") - return 0, 0, fmt.Errorf("failed to get CustodianLinkCount from DB: %w", err) - } - - // if the tx was created in this scope we will commit here - if err := commit(); err != nil { - return 0, 0, fmt.Errorf("failed to commit GetCustodianByWalletID transaction: %w", err) - } - - for _, linkingInfo := range li { - // find the right linking id - if linkingInfo.LinkingID.String() == linkingID.String() { - // max is wallets linked + open slots - return linkingInfo.WalletsLinked, linkingInfo.WalletsLinked + linkingInfo.OpenLinkingSlots, nil - } - } - // wallets linked/ open linking slots not found - // this is the case where there is no prior linkages - // 0 linked, get max from environment - return 0, getEnvMaxCards(custodian), nil - -} - -func rollbackFn(ctx context.Context, datastore Datastore, tx *sqlx.Tx) func() { - return func() { - logger(ctx).Debug().Msg("rolling back transaction") - datastore.RollbackTx(tx) - } -} - -func commitFn(ctx context.Context, tx *sqlx.Tx) func() error { - return func() error { - logger(ctx).Debug().Msg("committing transaction") - if err := tx.Commit(); err != nil { - logger(ctx).Error().Err(err).Msg("failed to commit transaction") - return err - } - return nil - } -} - -// getTx will get or create a tx on the context, if created hands back rollback and commit functions -func getTx(ctx context.Context, datastore Datastore) (context.Context, *sqlx.Tx, func(), func() error, error) { - // create a sublogger - sublogger := logger(ctx) - sublogger.Debug().Msg("getting tx from context") - // get tx - tx, noContextTx := ctx.Value(appctx.DatabaseTransactionCTXKey).(*sqlx.Tx) - if !noContextTx { - sublogger.Debug().Msg("no tx in context") - tx, err := createTx(ctx, datastore) - if err != nil || tx == nil { - sublogger.Error().Err(err).Msg("error creating tx") - return ctx, nil, func() {}, func() error { return nil }, fmt.Errorf("failed to create tx: %w", err) - } - ctx = context.WithValue(ctx, appctx.DatabaseTransactionCTXKey, tx) - return ctx, tx, rollbackFn(ctx, datastore, tx), commitFn(ctx, tx), nil - } - return ctx, tx, func() {}, func() error { return nil }, nil -} - -// GetCustodianLinkByWalletID retrieves the currently linked wallet custodian by walletID. -func (pg *Postgres) GetCustodianLinkByWalletID(ctx context.Context, ID uuid.UUID) (*CustodianLink, error) { - const q = ` - select - wc.wallet_id, wc.custodian, wc.linking_id, - wc.created_at, wc.disconnected_at, wc.linked_at - from - wallet_custodian wc - where - wc.wallet_id = $1 and - wc.disconnected_at is null and - wc.unlinked_at is null - ` - result := &CustodianLink{} - if err := pg.GetContext(ctx, result, q, ID); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.ErrNoWalletCustodian - } - return nil, err - } - - return result, nil -} - -// DisconnectCustodialWallet - disconnect the wallet's custodial id -func (pg *Postgres) DisconnectCustodialWallet(ctx context.Context, walletID uuid.UUID) error { - // create a sublogger - sublogger := logger(ctx).With(). - Str("wallet_id", walletID.String()). - Logger() - - sublogger.Debug(). - Msg("disconnecting custodial wallet") - - // get tx - ctx, tx, rollback, commit, err := getTx(ctx, pg) - if err != nil { - return fmt.Errorf("failed to create db transaction DisconnectCustodialWallet: %w", err) - } - // will rollback if tx created at this scope - defer rollback() - - // sql query to perform unlinking - stmt := ` - update - wallets - set - user_deposit_destination='', - user_deposit_account_provider=null - where - id=$1 - ` - // perform query - if _, err := tx.ExecContext( - ctx, - stmt, - walletID, - ); err != nil { - sublogger.Error().Err(err).Msg("failed to update wallet_custodian_id for wallet") - return err - } - - // set disconnected on the custodian link - stmt = ` - update - wallet_custodian - set - disconnected_at=now(), - updated_at=now() - where - wallet_id=$1 and - disconnected_at is null and - unlinked_at is null - ` - // perform query - if _, err := tx.ExecContext( - ctx, - stmt, - walletID, - ); err != nil { - sublogger.Error().Err(err).Msg("failed to update wallet_custodian_id for wallet") - return err - } - - // if the tx was created in this scope we will commit here - if err := commit(); err != nil { - return fmt.Errorf("failed to commit DisconnectCustodialWallet transaction: %w", err) - } - // done - return nil -} - -// ConnectCustodialWallet - create a record of a custodian wallet -func (pg *Postgres) ConnectCustodialWallet(ctx context.Context, cl *CustodianLink, depositDest string) error { - var err error - // create a sublogger - sublogger := logger(ctx).With(). - Str("wallet_id", cl.GetWalletIDString()). - Str("custodian", cl.Custodian). - Str("linking_id", cl.GetLinkingIDString()). - Logger() - - sublogger.Debug(). - Msg("creating linking of wallet custodian") - - // get tx - ctx, tx, rollback, commit, err := getTx(ctx, pg) - if err != nil { - return fmt.Errorf("failed to create db transaction ConnectCustodialWallet: %w", err) - } - // will rollback if tx created at this scope - defer rollback() - - var existingLinkingID uuid.UUID - // get the custodial provider's linking id from db - stmt := ` - select linking_id from wallet_custodian - where wallet_id=$1 and custodian=$2 and - unlinked_at is null - ` - err = tx.Get(&existingLinkingID, stmt, cl.WalletID, cl.Custodian) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - sublogger.Error().Err(err). - Msg("failed to get linking id from wallet_custodian") - return fmt.Errorf("failed to get linking id from custodian record: %w", err) - } - - if !uuid.Equal(existingLinkingID, *new(uuid.UUID)) { - // check if the member matches the associated member - if !uuid.Equal(*cl.LinkingID, existingLinkingID) { - return handlers.WrapError(errors.New("wallets do not match"), "mismatched provider accounts", http.StatusForbidden) - } - } else { - // if the existingLinkingID is null then we need to check the linking limits - - // get the count - used, max, err := pg.GetCustodianLinkCount(ctx, *cl.LinkingID, cl.Custodian) - if err != nil { - sublogger.Error().Err(err). - Msg("failed to insert wallet_custodian due to db err checking linking limits") - return fmt.Errorf("failed to insert wallet custodian record due to db err checking linking limits: %w", err) - } - - // this will be the 10th linking - if used == 9 { - defer tenLinkagesReached.Inc() - } - - // check for linking limit - if used >= max { - sentry.WithScope(func(scope *sentry.Scope) { - scope.SetTags(map[string]string{ - "wallet_id": cl.WalletID.String(), - "linking_id": cl.LinkingID.String(), - }) - tooManyCardsCounter.Inc() - }) - return ErrTooManyCardsLinked - } - - // check the linking limit does not exceed what is appropriate - if err != nil { - sublogger.Error().Err(err). - Msg("failed to insert wallet_custodian due to db err checking linking limits") - return fmt.Errorf("failed to insert wallet custodian record due to db err checking linking limits: %w", err) - } - } - - // evict any prior linkings that were not disconnected (across all custodians) - stmt = ` - update wallet_custodian set disconnected_at=now(), updated_at=now() where wallet_id=$1 and disconnected_at is null - ` - // perform query - if _, err := tx.ExecContext(ctx, stmt, cl.WalletID); err != nil { - sublogger.Error().Err(err). - Msg("failed to update wallet_custodian evicting prior linked") - return fmt.Errorf("error updating wallet_custodian evicting prior linked: %w", err) - } - - stmt = ` - insert into wallet_custodian ( - wallet_id, custodian, linking_id - ) values ( - $1, $2, $3 - ) - on conflict (wallet_id, custodian, linking_id) - do update set updated_at=now(), disconnected_at=null, unlinked_at=null, linked_at=now() - returning * - ` - - err = tx.Get(cl, stmt, cl.WalletID, cl.Custodian, cl.LinkingID) - if err != nil { - sublogger.Error().Err(err). - Msg("failed to insert wallet_custodian") - return fmt.Errorf("failed to insert wallet custodian record: %w", err) - } - // update wallets with new deposit destination - stmt = ` - update wallets set - user_deposit_destination=$1,provider_linking_id=$2,user_deposit_account_provider=$3 - where id=$4 - ` - // perform query - if r, err := tx.ExecContext( - ctx, - stmt, - depositDest, - cl.LinkingID, - cl.Custodian, - cl.WalletID, - ); err != nil { - sublogger.Error().Err(err). - Msg("failed to update wallets with new deposit destination") - return fmt.Errorf("error updating wallets with new deposit desintation: %w", err) - } else if r != nil { - count, _ := r.RowsAffected() - if count < 1 { - sublogger.Error().Msg("at least one record should be updated for connecting a verified wallet") - return errors.New("should have updated at least one wallet for connecting a verified wallet") - } - } - - // if the tx was created in this scope we will commit here - if err := commit(); err != nil { - return fmt.Errorf("failed to commit ConnectCustodialWallet transaction: %w", err) - } - return nil -} - -// InsertVerifiedWalletOutboxTx inserts a verifiedWalletOutbox for processing. -func (pg *Postgres) InsertVerifiedWalletOutboxTx(ctx context.Context, tx *sqlx.Tx, walletID uuid.UUID, verifiedWallet bool) error { - _, err := tx.ExecContext(ctx, `insert into verified_wallet_outbox(payment_id, verified_wallet) - values ($1, $2)`, walletID, verifiedWallet) - if err != nil { - return fmt.Errorf("error inserting values into vefified wallet outbox: %w", err) - } - return nil -} - -// SendVerifiedWalletOutbox sends requests to reputation service. -func (pg *Postgres) SendVerifiedWalletOutbox(ctx context.Context, client reputation.Client, retry backoff.RetryFunc) (bool, error) { - vw := struct { - ID uuid.UUID `db:"id"` - PaymentID uuid.UUID `db:"payment_id"` - VerifiedWallet bool `db:"verified_wallet"` - }{} - - _, tx, rollback, commit, err := datastore.GetTx(ctx, pg) - if err != nil { - return false, fmt.Errorf("error getting tx for verified wallet: %w", err) - } - defer rollback() - - err = tx.Get(&vw, `select id, payment_id, verified_wallet from verified_wallet_outbox - order by created_at asc for update skip locked limit 1`) - if err != nil { - return false, fmt.Errorf("error get verified wallet: %w", err) - } - - upsertReputationSummaryOp := func() (interface{}, error) { - return nil, client.UpdateReputationSummary(ctx, vw.PaymentID.String(), vw.VerifiedWallet) - } - - _, err = retry(ctx, upsertReputationSummaryOp, retryPolicy, canRetry(nonRetriableErrors)) - if err != nil { - return true, fmt.Errorf("error calling reputation for verified wallet: %w", err) - } - - _, err = tx.ExecContext(ctx, "delete from verified_wallet_outbox where id = $1", vw.ID) - if err != nil { - return true, fmt.Errorf("error deleting verified wallet txn: %w", err) - } - - err = commit() - if err != nil { - return true, fmt.Errorf("error commit verified wallet txn: %w", err) - } - - return true, nil -} - -// helper to make logger easier -func logger(ctx context.Context) *zerolog.Logger { - // get logger - return logging.Logger(ctx, "wallet") -} - -// helper to create a tx -func createTx(ctx context.Context, datastore Datastore) (tx *sqlx.Tx, err error) { - logger(ctx).Debug(). - Msg("creating transaction") - tx, err = datastore.RawDB().Beginx() - if err != nil { - logger(ctx).Error().Err(err). - Msg("error creating transaction") - return tx, fmt.Errorf("failed to create transaction: %w", err) - } - return tx, nil -} - -// acquire tx advisory lock for id automatically released when tx ends -func waitAndLockTx(ctx context.Context, tx *sqlx.Tx, id uuid.UUID) error { - query := "SELECT pg_advisory_xact_lock(hashtext($1))" - _, err := tx.ExecContext(ctx, query, id.String()) - if err != nil { - return fmt.Errorf("failed to acquire tx lock id %s: %w", id.String(), err) - } - return nil -} diff --git a/services/wallet/datastore_pvt_test.go b/services/wallet/datastore_pvt_test.go deleted file mode 100644 index 69365b176..000000000 --- a/services/wallet/datastore_pvt_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package wallet - -import ( - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/ptr" - should "github.com/stretchr/testify/assert" -) - -func TestCustodianLink_isLinked(t *testing.T) { - type tcGiven struct { - cl *CustodianLink - } - - type testCase struct { - name string - given tcGiven - expected bool - } - - tests := []testCase{ - { - name: "is_linked", - given: tcGiven{ - cl: &CustodianLink{ - LinkedAt: time.Now(), - }}, - expected: true, - }, - { - name: "is_not_linked_nil", - given: tcGiven{}, - }, - { - name: "is_not_linked", - given: tcGiven{ - cl: &CustodianLink{ - LinkedAt: time.Now(), - UnlinkedAt: ptr.To(time.Now()), - }}, - }, - { - name: "is_not_linked_disconnected", - given: tcGiven{ - cl: &CustodianLink{ - LinkedAt: time.Now(), - DisconnectedAt: ptr.To(time.Now()), - }}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - actual := tc.given.cl.isLinked() - should.Equal(t, tc.expected, actual) - }) - } -} diff --git a/services/wallet/datastore_test.go b/services/wallet/datastore_test.go deleted file mode 100644 index 477ee9136..000000000 --- a/services/wallet/datastore_test.go +++ /dev/null @@ -1,418 +0,0 @@ -//go:build integration - -package wallet - -import ( - "context" - "errors" - "fmt" - "sync" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/libs/backoff/retrypolicy" - mock_reputation "github.com/brave-intl/bat-go/libs/clients/reputation/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/datastore" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/services/wallet/model" - "github.com/golang/mock/gomock" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/suite" -) - -type WalletPostgresTestSuite struct { - suite.Suite -} - -func TestWalletPostgresTestSuite(t *testing.T) { - suite.Run(t, new(WalletPostgresTestSuite)) -} - -func (suite *WalletPostgresTestSuite) SetupSuite() { - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - m, err := pg.NewMigrate() - suite.Require().NoError(err, "Failed to create migrate instance") - - ver, dirty, _ := m.Version() - if dirty { - suite.Require().NoError(m.Force(int(ver))) - } - if ver > 0 { - suite.Require().NoError(m.Down(), "Failed to migrate down cleanly") - } - - suite.Require().NoError(pg.Migrate(), "Failed to fully migrate") -} - -func (suite *WalletPostgresTestSuite) SetupTest() { - suite.CleanDB() -} - -func (suite *WalletPostgresTestSuite) TearDownTest() { - suite.CleanDB() -} - -func (suite *WalletPostgresTestSuite) CleanDB() { - tables := []string{"claim_creds", "claims", "wallets", "issuers", "promotions"} - - pg, _, err := NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - for _, table := range tables { - _, err = pg.RawDB().Exec("delete from " + table) - suite.Require().NoError(err, "Failed to get clean table") - } -} - -func (suite *WalletPostgresTestSuite) TestInsertWallet() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - wallet := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(pg.InsertWallet(context.Background(), wallet), "Save wallet should succeed") -} - -func (suite *WalletPostgresTestSuite) TestUpsertWallet() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - - wallet := &walletutils.Info{ID: uuid.NewV4().String(), Provider: "uphold", ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(pg.UpsertWallet(context.Background(), wallet), "Save wallet should succeed") -} - -func (suite *WalletPostgresTestSuite) TestGetWallet() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - id := uuid.NewV4() - - tmp := altcurrency.BAT - origWallet := &walletutils.Info{ID: id.String(), Provider: "uphold", AltCurrency: &tmp, ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(pg.UpsertWallet(context.Background(), origWallet), "Save wallet should succeed") - - wallet, err := pg.GetWallet(context.Background(), id) - suite.Require().NoError(err, "Get wallet should succeed") - suite.Assert().Equal(origWallet, wallet) -} - -func (suite *WalletPostgresTestSuite) TestCustodianLink() { - - ctx := context.WithValue(context.Background(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - // setup a wallet - publicKey := "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu3jMwryY=" - id := uuid.NewV4() - depositDest := uuid.NewV4() - linkingID := uuid.NewV4() - - tmp := altcurrency.BAT - origWallet := &walletutils.Info{ID: id.String(), Provider: "uphold", AltCurrency: &tmp, ProviderID: uuid.NewV4().String(), PublicKey: publicKey} - suite.Require().NoError(pg.UpsertWallet(context.Background(), origWallet), "Save wallet should succeed") - - // perform a connect custodial wallet - suite.Require().NoError( - pg.ConnectCustodialWallet(ctx, &CustodianLink{ - WalletID: &id, - Custodian: "gemini", - LinkingID: &linkingID, - }, depositDest.String()), - "connect custodial wallet should succeed") - - // get the wallet and check that the custodian link entry id is right - // get the custodian link entry and validate that the data is correct - cl, err := pg.GetCustodianLinkByWalletID(ctx, id) - suite.Require().NoError(err, "should have no error getting custodian link") - suite.Require().True(cl.LinkingID.String() == linkingID.String(), "linking id is not right") - suite.Require().True(cl.WalletID.String() == id.String(), "wallet id is not right") - suite.Require().True(cl.Custodian == "gemini", "custodian is not right") - - // check the link count is 1 for this wallet - used, max, err := pg.GetCustodianLinkCount(ctx, linkingID, "gemini") - suite.Require().NoError(err, "should have no error getting custodian link count") - - // disconnect the wallet - suite.Require().NoError( - pg.DisconnectCustodialWallet(ctx, id), - "connect custodial wallet should succeed") - - // connect a custodial wallet to make sure not more than one linking is added for same cust/wallet - suite.Require().NoError( - pg.ConnectCustodialWallet(ctx, &CustodianLink{ - WalletID: &id, - Custodian: "gemini", - LinkingID: &linkingID, - }, depositDest.String()), - "connect custodial wallet should succeed") - - // only one slot should be taken - suite.Require().True(used == 1, "linking count is not right") - suite.Require().True(max == getEnvMaxCards("gemini"), "linking count is not right") - - // perform a disconnect custodial wallet - suite.Require().NoError( - pg.DisconnectCustodialWallet(ctx, id), - "disconnect custodial wallet should succeed") - - // should return sql not found error after a disconnect - cl, err = pg.GetCustodianLinkByWalletID(ctx, id) - suite.Require().True(errors.Is(err, model.ErrNoWalletCustodian), "should be no rows found error") -} - -func (suite *WalletPostgresTestSuite) TestConnectCustodialWallet_Rollback() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - ctx := context.Background() - - walletID := uuid.NewV4() - linkingID := uuid.NewV4() - depositDest := uuid.NewV4().String() - - err = pg.ConnectCustodialWallet(ctx, &CustodianLink{ - WalletID: &walletID, - Custodian: "uphold", - LinkingID: &linkingID, - }, depositDest) - - suite.Require().True(err != nil, "should have returned error") - - count, _, err := pg.GetCustodianLinkCount(ctx, linkingID, "") - - suite.Require().NoError(err) - suite.Require().True(count == 0, "should have performed rollback on connect custodial wallet") -} - -func (suite *WalletPostgresTestSuite) TestLinkWallet_Concurrent_InsertUpdate() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - repClient := mock_reputation.NewMockClient(mockCtrl) - repClient.EXPECT().IsLinkingReputable(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - - ctx := context.WithValue(context.Background(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, repClient) - - for i := 0; i < 1; i++ { - // seed 3 wallets with same linkingID - userDepositDestination, providerLinkingID := suite.seedWallet(pg) - - // concurrently link new wallet with same linkingID - altCurrency := altcurrency.BAT - walletInfo := &walletutils.Info{ - ID: uuid.NewV4().String(), - Provider: "uphold", - ProviderID: uuid.NewV4().String(), - AltCurrency: &altCurrency, - PublicKey: "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu1jMwryY=", - } - - err = pg.UpsertWallet(ctx, walletInfo) - suite.Require().NoError(err, "save wallet should succeed") - - runs := 2 - var wg sync.WaitGroup - wg.Add(runs) - - for i := 0; i < runs; i++ { - go func() { - defer wg.Done() - err = pg.LinkWallet(ctx, walletInfo.ID, userDepositDestination, providerLinkingID, walletInfo.Provider, "") - }() - } - wg.Wait() - - used, max, err := pg.GetCustodianLinkCount(ctx, providerLinkingID, "") - - suite.Require().NoError(err, "should have no error getting custodian link count") - suite.Require().True(used == max, fmt.Sprintf("used %d should not exceed max %d", used, max)) - } -} - -func (suite *WalletPostgresTestSuite) seedWallet(pg Datastore) (string, uuid.UUID) { - userDepositDestination := uuid.NewV4().String() - providerLinkingID := uuid.NewV4() - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - repClient := mock_reputation.NewMockClient(mockCtrl) - repClient.EXPECT().IsLinkingReputable(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - - ctx := context.WithValue(context.Background(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, repClient) - - walletCount := 3 - for i := 0; i < walletCount; i++ { - altCurrency := altcurrency.BAT - walletInfo := &walletutils.Info{ - ID: uuid.NewV4().String(), - Provider: "uphold", - ProviderID: uuid.NewV4().String(), - AltCurrency: &altCurrency, - PublicKey: "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu1jMwryY=", - AnonymousAddress: nil, - } - - err := pg.UpsertWallet(ctx, walletInfo) - suite.Require().NoError(err, "save wallet should succeed") - - err = pg.LinkWallet(ctx, walletInfo.ID, userDepositDestination, providerLinkingID, "uphold", "") - suite.Require().NoError(err, "link wallet should succeed") - } - - used, _, err := pg.GetCustodianLinkCount(ctx, providerLinkingID, "") - - suite.Require().NoError(err, "should have no error getting custodian link count") - suite.Require().True(used == walletCount, fmt.Sprintf("used %d", used)) - - return userDepositDestination, providerLinkingID -} - -func (suite *WalletPostgresTestSuite) TestLinkWallet_Concurrent_MaxLinkCount() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - repClient := mock_reputation.NewMockClient(mockCtrl) - repClient.EXPECT().IsLinkingReputable(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - - ctx := context.WithValue(context.Background(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, repClient) - - wallets := make([]*walletutils.Info, 10, 10) - - for i := 0; i < len(wallets); i++ { - altCurrency := altcurrency.BAT - walletInfo := &walletutils.Info{ - ID: uuid.NewV4().String(), - Provider: "uphold", - ProviderID: uuid.NewV4().String(), - AltCurrency: &altCurrency, - PublicKey: "hBrtClwIppLmu/qZ8EhGM1TQZUwDUosbOrVu1jMwryY=", - } - wallets[i] = walletInfo - err := pg.UpsertWallet(ctx, walletInfo) - suite.Require().NoError(err, "save wallet should succeed") - } - - var wg sync.WaitGroup - wg.Add(len(wallets)) - - userDepositDestination := uuid.NewV4().String() - providerLinkingID := uuid.NewV4() - - for i := 0; i < len(wallets); i++ { - go func(index int) { - defer wg.Done() - err = pg.LinkWallet(ctx, wallets[index].ID, userDepositDestination, providerLinkingID, wallets[index].Provider, "") - }(i) - } - wg.Wait() - - used, max, err := pg.GetCustodianLinkCount(ctx, providerLinkingID, "") - - suite.Require().NoError(err, "should have no error getting custodian link count") - suite.Require().True(used == max, fmt.Sprintf("used %d should not exceed max %d", used, max)) -} - -func (suite *WalletPostgresTestSuite) TestWaitAndLock() { - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - actual := make(chan int, 2) - - f := func(ctx context.Context, waitSeconds int, process int, lockID uuid.UUID) { - tx, err := pg.RawDB().Beginx() - suite.Require().NoError(err) - - err = waitAndLockTx(ctx, tx, lockID) - suite.Require().NoError(err) - - _, err = tx.ExecContext(ctx, "SELECT 1, pg_sleep($1)", waitSeconds) - suite.Require().NoError(err) - - actual <- process - - err = tx.Commit() - suite.Require().NoError(err) - } - - lockID := uuid.NewV4() - - go f(context.Background(), 2, 1, lockID) - time.Sleep(500 * time.Millisecond) - go f(context.Background(), 0, 2, lockID) - - suite.Require().True(<-actual == 1) - suite.Require().True(<-actual == 2) - - row := pg.RawDB().QueryRow("SELECT COUNT(*) FROM pg_locks pl WHERE pl.objid = hashtext($1)", lockID) - - var lockCount int - err = row.Scan(&lockCount) - suite.Require().True(lockCount == 0, fmt.Sprintf("should have released all locks but found %d", lockCount)) -} - -func (suite *WalletPostgresTestSuite) TestSendVerifiedWalletOutbox() { - ctx := context.Background() - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - pg, _, err := NewPostgres() - suite.Require().NoError(err) - - repClient := mock_reputation.NewMockClient(ctrl) - - _, tx, _, commit, err := datastore.GetTx(ctx, pg) - suite.Require().NoError(err) - - var paymentIDs []uuid.UUID - for i := 0; i < 5; i++ { - - // Insert the outbox request. - paymentIDs = append(paymentIDs, uuid.NewV4()) - err = pg.InsertVerifiedWalletOutboxTx(ctx, tx, paymentIDs[i], true) - suite.Require().NoError(err) - - // Mock the reputation call, the requests are ordered by created at so should be - // processed in the order of insertion. - repClient.EXPECT(). - UpdateReputationSummary(ctx, paymentIDs[i].String(), true). - Return(nil) - } - - err = commit() - suite.Require().NoError(err) - - // Send the requests - retryPolicy = retrypolicy.NoRetry - for i := 0; i < 5; i++ { - _, err = pg.SendVerifiedWalletOutbox(ctx, repClient, backoff.Retry) - suite.Require().NoError(err) - } - - // Assert all request have been processed, the table should be empty. - var count int - err = pg.RawDB().GetContext(ctx, &count, `select count(*) from verified_wallet_outbox`) - suite.Require().NoError(err) - suite.Assert().Equal(0, count) -} diff --git a/services/wallet/docker-compose.yml b/services/wallet/docker-compose.yml deleted file mode 100644 index 9adc43570..000000000 --- a/services/wallet/docker-compose.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: "3.4" - -networks: - wallet: - driver: bridge - -services: - # dev-refresh service will start up a rewards server bound to host port 3343 - # which allows one to do `docker restart rewards-dev-refresh` when the user - # wants to "restart" the service running new code. This is especially helpful - # when you hook it up to `fswatch` type utilities, causing a re-run of `go run` - # every time a file changes. - wallet-dev-refresh: - container_name: wallet-dev-refresh - image: golang:1.19 - ports: - - "3353:3353" - - "6061:6061" - security_opt: - - no-new-privileges:true - read_only: true - command: "cd main && go run main.go serve wallet rest" - volumes: - - ./:/src - working_dir: /src - networks: - - wallet - - grant - depends_on: - - postgres - environment: - - PPROF_ENABLED - - MIGRATE=true - - ENV=local - - ADDR=:3353 - - DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable - - RO_DATABASE_URL=postgres://grants:password@postgres/grants?sslmode=disable - - UPHOLD_ACCESS_TOKEN diff --git a/services/wallet/gemini.go b/services/wallet/gemini.go deleted file mode 100644 index ab8d3da59..000000000 --- a/services/wallet/gemini.go +++ /dev/null @@ -1,72 +0,0 @@ -package wallet - -import ( - "context" - "strings" - - "github.com/brave-intl/bat-go/libs/clients/gemini" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - errorutils "github.com/brave-intl/bat-go/libs/errors" -) - -type geminix struct { - docTypes []string -} - -func newGeminix(docTypePrecedence ...string) *geminix { - return &geminix{docTypes: docTypePrecedence} -} - -// GetIssuingCountry returns the issuing country for the provided gemini.ValidatedAccount. -// -// GetIssuingCountry will primarily try to use the valid documents attached to the account, -// if no valid documents are associated with the account and the fallback param is true then the account -// country code will be used. -// -// GetIssuingCountry returns an empty string when no accepted document types are associated with the account. -func (x *geminix) GetIssuingCountry(acc gemini.ValidatedAccount, fallback bool) string { - var issuingCountry string - - if fallback { - issuingCountry = acc.CountryCode - } - - if len(acc.ValidDocuments) > 0 { - issuingCountry = countryForDocByPrecedence(x.docTypes, acc.ValidDocuments) - } - - return issuingCountry -} - -func countryForDocByPrecedence(precedence []string, docs []gemini.ValidDocument) string { - var result string - - for _, pdoc := range precedence { - for _, vdoc := range docs { - if strings.EqualFold(pdoc, vdoc.Type) { - return strings.ToUpper(vdoc.IssuingCountry) - } - } - } - - return result -} - -func (x *geminix) IsRegionAvailable(ctx context.Context, issuingCountry string, custodianRegions custodian.Regions) error { - if useCustodianRegions, ok := ctx.Value(appctx.UseCustodianRegionsCTXKey).(bool); ok && useCustodianRegions { - allowed := custodianRegions.Gemini.Verdict(issuingCountry) - if !allowed { - return errorutils.ErrInvalidCountry - } - } else { - if blacklist, ok := ctx.Value(appctx.BlacklistedCountryCodesCTXKey).([]string); ok { - for _, v := range blacklist { - if strings.EqualFold(issuingCountry, v) { - return errorutils.ErrInvalidCountry - } - } - } - } - return nil -} diff --git a/services/wallet/gemini_test.go b/services/wallet/gemini_test.go deleted file mode 100644 index ccc34a617..000000000 --- a/services/wallet/gemini_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package wallet - -import ( - "testing" - - "github.com/brave-intl/bat-go/libs/clients/gemini" - should "github.com/stretchr/testify/assert" -) - -func TestGetIssuingCountry(t *testing.T) { - type tcGiven struct { - gx *geminix - validAcc gemini.ValidatedAccount - fallback bool - } - - tests := []struct { - name string - given tcGiven - expected string - }{ - { - name: "has_prior_linking_no_valid_documents", - given: tcGiven{ - gx: newGeminix("passport"), - validAcc: gemini.ValidatedAccount{ - CountryCode: "US", - }, - fallback: true, - }, - expected: "US", - }, - { - name: "has_prior_linking_and_valid_documents", - given: tcGiven{ - gx: newGeminix("passport"), - validAcc: gemini.ValidatedAccount{ - CountryCode: "US", - ValidDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "PT", - }, - }, - }, - fallback: true, - }, - expected: "PT", - }, - { - name: "has_no_prior_linking_and_no_valid_documents", - given: tcGiven{ - gx: newGeminix("passport"), - validAcc: gemini.ValidatedAccount{ - CountryCode: "US", - }, - fallback: false, - }, - expected: "", - }, - { - name: "has_no_prior_linking_and_valid_documents", - given: tcGiven{ - gx: newGeminix("passport"), - validAcc: gemini.ValidatedAccount{ - CountryCode: "US", - ValidDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "PT", - }, - }, - }, - fallback: false, - }, - expected: "PT", - }, - { - name: "has_prior_linking_and_no_country_code_and_no_valid_documents", - given: tcGiven{ - gx: newGeminix("passport"), - validAcc: gemini.ValidatedAccount{}, - fallback: false, - }, - expected: "", - }, - { - name: "has_prior_linking_and_no_country_code_and_valid_documents", - given: tcGiven{ - gx: newGeminix("passport"), - validAcc: gemini.ValidatedAccount{ - ValidDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "PT", - }, - }, - }, - fallback: true, - }, - expected: "PT", - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - actual := tc.given.gx.GetIssuingCountry(tc.given.validAcc, tc.given.fallback) - should.Equal(t, tc.expected, actual) - }) - } -} - -func TestCountryForDocByPrecedence(t *testing.T) { - type tcGiven struct { - docTypePres []string - validDocuments []gemini.ValidDocument - } - - type testCase struct { - name string - given tcGiven - exp string - } - - tests := []testCase{ - { - name: "empty", - }, - - { - name: "one_passport", - given: tcGiven{ - docTypePres: []string{ - "passport", - "drivers_license", - "national_identity_card", - "passport_card", - }, - validDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "US", - }, - }, - }, - exp: "US", - }, - - { - name: "two_docs", - given: tcGiven{ - docTypePres: []string{ - "passport", - "drivers_license", - "national_identity_card", - "passport_card", - }, - validDocuments: []gemini.ValidDocument{ - { - Type: "passport", - IssuingCountry: "US", - }, - - { - Type: "drivers_license", - IssuingCountry: "CA", - }, - }, - }, - exp: "US", - }, - - { - name: "two_docs_reverse", - given: tcGiven{ - docTypePres: []string{ - "passport", - "drivers_license", - "national_identity_card", - "passport_card", - }, - validDocuments: []gemini.ValidDocument{ - { - Type: "drivers_license", - IssuingCountry: "CA", - }, - - { - Type: "passport", - IssuingCountry: "US", - }, - }, - }, - exp: "US", - }, - - { - name: "no_valid_document_type", - given: tcGiven{ - docTypePres: []string{ - "passport", - "drivers_license", - "national_identity_card", - "passport_card", - }, - validDocuments: []gemini.ValidDocument{ - { - Type: "invalid_type", - IssuingCountry: "US", - }, - }, - }, - exp: "", - }, - - { - name: "valid_and_invalid_document_type_lower_case", - given: tcGiven{ - docTypePres: []string{ - "passport", - "drivers_license", - "national_identity_card", - "passport_card", - }, - validDocuments: []gemini.ValidDocument{ - { - Type: "invalid_type", - IssuingCountry: "US", - }, - { - Type: "passport", - IssuingCountry: "uk", - }, - }, - }, - exp: "UK", - }, - } - - for i := range tests { - tc := tests[i] - t.Run(tc.name, func(t *testing.T) { - act := countryForDocByPrecedence(tc.given.docTypePres, tc.given.validDocuments) - should.Equal(t, tc.exp, act) - }) - } -} diff --git a/services/wallet/geocountry.go b/services/wallet/geocountry.go deleted file mode 100644 index 7d09397dc..000000000 --- a/services/wallet/geocountry.go +++ /dev/null @@ -1,65 +0,0 @@ -package wallet - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - "github.com/aws/aws-sdk-go-v2/service/s3" - appaws "github.com/brave-intl/bat-go/libs/aws" - "github.com/brave-intl/bat-go/libs/logging" -) - -// Config defines a GeoCountryValidator configuration. -type Config struct { - bucket string - object string -} - -// GeoCountryValidator defines a GeoCountryValidator. -type GeoCountryValidator struct { - s3 appaws.S3GetObjectAPI - config Config -} - -// NewGeoCountryValidator creates a new instance of NewGeoCountryValidator. -func NewGeoCountryValidator(s3 appaws.S3GetObjectAPI, config Config) *GeoCountryValidator { - return &GeoCountryValidator{ - s3: s3, - config: config, - } -} - -// Validate is an implementation of the Validate interface and returns true is a given geo country is valid. -func (g GeoCountryValidator) Validate(ctx context.Context, geoCountry string) (bool, error) { - out, err := g.s3.GetObject( - ctx, &s3.GetObjectInput{ - Bucket: &g.config.bucket, - Key: &g.config.object, - }) - if err != nil { - return false, fmt.Errorf("error failed to get s3 object: %w", err) - } - defer func() { - err := out.Body.Close() - if err != nil { - logging.FromContext(ctx).Error(). - Err(err).Msg("error closing body") - } - }() - - var locations []string - err = json.NewDecoder(out.Body).Decode(&locations) - if err != nil { - return false, fmt.Errorf("error decoding geo country s3 list") - } - - for _, location := range locations { - if strings.EqualFold(location, geoCountry) { - return false, nil - } - } - - return true, nil -} diff --git a/services/wallet/geocountry_test.go b/services/wallet/geocountry_test.go deleted file mode 100644 index 5d53511d5..000000000 --- a/services/wallet/geocountry_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package wallet - -import ( - "bytes" - "context" - "encoding/json" - "io" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/s3" - mockaws "github.com/brave-intl/bat-go/libs/aws/mock" - "github.com/brave-intl/bat-go/libs/test" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -func TestGeoCountryValidator_Validate_Enabled(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - disabledGeoCountries := []string{ - test.RandomString(), - test.RandomString(), - test.RandomString(), - test.RandomString(), - test.RandomString(), - } - - b, err := json.Marshal(disabledGeoCountries) - assert.NoError(t, err) - - buffer := bytes.NewBuffer(b) - body := io.NopCloser(buffer) - - out := &s3.GetObjectOutput{ - Body: body, - } - - api := mockaws.NewMockS3GetObjectAPI(ctrl) - api.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any()). - Return(out, nil) - - config := Config{ - bucket: test.RandomString(), - object: test.RandomString(), - } - - g := NewGeoCountryValidator(api, config) - - enabled, err := g.Validate(context.Background(), test.RandomString()) - assert.NoError(t, err) - - assert.True(t, enabled) -} - -func TestGeoCountryValidator_Validate_Disabled(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - disabledGeoCountries := []string{ - test.RandomString(), - test.RandomString(), - test.RandomString(), - test.RandomString(), - test.RandomString(), - } - - b, err := json.Marshal(disabledGeoCountries) - assert.NoError(t, err) - - buffer := bytes.NewBuffer(b) - body := io.NopCloser(buffer) - - out := &s3.GetObjectOutput{ - Body: body, - } - - api := mockaws.NewMockS3GetObjectAPI(ctrl) - api.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any()). - Return(out, nil) - - config := Config{ - bucket: test.RandomString(), - object: test.RandomString(), - } - - g := NewGeoCountryValidator(api, config) - - enabled, err := g.Validate(context.Background(), disabledGeoCountries[3]) - assert.NoError(t, err) - - assert.False(t, enabled) -} diff --git a/services/wallet/inputs.go b/services/wallet/inputs.go deleted file mode 100644 index f01004fe3..000000000 --- a/services/wallet/inputs.go +++ /dev/null @@ -1,482 +0,0 @@ -package wallet - -import ( - "context" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - "github.com/asaskevich/govalidator" - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/inputs" - "github.com/brave-intl/bat-go/libs/middleware" - "gopkg.in/square/go-jose.v2/jwt" -) - -var ( - // ErrMissingSignedCreationRequest - required parameter missing from request - ErrMissingSignedCreationRequest = errors.New("missing signed creation request") - // ErrMissingSignedLinkingRequest - required parameter missing from request - ErrMissingSignedLinkingRequest = errors.New("missing signed linking request") - // ErrInvalidJSON - the input json is invalid - ErrInvalidJSON = errors.New("invalid json") - // ErrMissingLinkingInfo - required parameter missing from request - ErrMissingLinkingInfo = errors.New("missing linking information") - ErrZebPayInvalidVrfToken = errors.New("failed to validate 'linking_info': must not be empty") -) - -// CustodianName - input validation for custodian name -type CustodianName string - -// String - implement the stringer interface for this input -func (cn *CustodianName) String() string { - return string(*cn) -} - -// Validate - implement the validatable interface for this input -func (cn *CustodianName) Validate(ctx context.Context) error { - if string(*cn) != "uphold" && string(*cn) != "bitflyer" && string(*cn) != "brave" && string(*cn) != "gemini" { - return fmt.Errorf("validate custodian name not in (uphold, bitflyer, brave, gemini)") - } - return nil -} - -// Decode - implement the decodable interface for this input -func (cn *CustodianName) Decode(ctx context.Context, v []byte) error { - *cn = CustodianName(string(v)) - if *cn == "" { - return fmt.Errorf("failed to decode custodian name, cannot be empty") - } - return nil -} - -// UpholdCreationRequest - the structure for a brave provider wallet creation request -type UpholdCreationRequest struct { - SignedCreationRequest string `json:"signedCreationRequest"` - PublicKey string `json:"-"` -} - -// Validate - implementation of validatable interface -func (ucr *UpholdCreationRequest) Validate(ctx context.Context) error { - // validate there is a signed creation request - if ucr.SignedCreationRequest == "" { - return ErrMissingSignedCreationRequest - } - return nil -} - -// Decode - implementation of decodable interface -func (ucr *UpholdCreationRequest) Decode(ctx context.Context, v []byte) error { - if err := inputs.DecodeJSON(ctx, v, ucr); err != nil { - return fmt.Errorf("failed to decode json: %w", err) - } - // extract public key from the base64 encoded signing request headers - - b, err := base64.StdEncoding.DecodeString(ucr.SignedCreationRequest) - if err != nil { - return fmt.Errorf("failed to decode signed creation request: %w", err) - } - - var signedTx httpsignature.HTTPSignedRequest - err = json.Unmarshal(b, &signedTx) - if err != nil { - return fmt.Errorf("failed to decode signed creation request: %w", err) - } - - _, err = govalidator.ValidateStruct(signedTx) - if err != nil { - return fmt.Errorf("failed to decode signed creation request: %w", err) - } - - var body map[string]interface{} - err = json.Unmarshal([]byte(signedTx.Body), &body) - if err != nil { - return fmt.Errorf("failed to decode signed creation request: %w", err) - } - - pk, exists := body["publicKey"] - if !exists { - return errors.New("failed to decode signed creation request: no publicKey in body") - } - - publicKey, ok := pk.(string) - if !ok { - return errors.New("failed to decode signed creation request: bad publicKey in body") - } - - // put public key from request in ucr.PublicKey - ucr.PublicKey = publicKey - - return nil -} - -// HandleErrors - handle any errors from this request -func (ucr *UpholdCreationRequest) HandleErrors(err error) *handlers.AppError { - issues := map[string]string{} - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - if strings.Contains(e.Error(), "failed decoding") { - issues["decoding"] = e.Error() - } - if strings.Contains(e.Error(), "failed validation") { - issues["validation"] = e.Error() - } - if errors.Is(e, ErrMissingSignedCreationRequest) { - issues["signedCreationRequest"] = "value is required" - } - } - } - return handlers.ValidationError("uphold create wallet request validation errors", issues) -} - -// BraveCreationRequest - the structure for a brave provider wallet creation request -type BraveCreationRequest struct{} - -// Validate - implementation of validatable interface -func (bcr *BraveCreationRequest) Validate(ctx context.Context) error { - return nil -} - -// Decode - implementation of decodable interface -func (bcr *BraveCreationRequest) Decode(ctx context.Context, v []byte) error { - return nil -} - -// HandleErrors - handle any errors from this request -func (bcr *BraveCreationRequest) HandleErrors(err error) *handlers.AppError { - issues := map[string]string{} - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - if strings.Contains(e.Error(), "failed decoding") { - issues["decoding"] = e.Error() - } - if strings.Contains(e.Error(), "failed validation") { - issues["validation"] = e.Error() - } - } - } - return handlers.ValidationError("brave create wallet request validation errors", issues) -} - -// LinkUpholdDepositAccountRequest - the structure for a linking request for uphold deposit account -type LinkUpholdDepositAccountRequest struct { - SignedLinkingRequest string `json:"signedLinkingRequest"` - AnonymousAddress string `json:"anonymousAddress"` -} - -// Validate - implementation of validatable interface -func (ludar *LinkUpholdDepositAccountRequest) Validate(ctx context.Context) error { - var merr = new(errorutils.MultiError) - if ludar.SignedLinkingRequest == "" { - merr.Append(errors.New("failed to validate 'signedLinkingRequest': must not be empty")) - } - if ludar.AnonymousAddress != "" && !govalidator.IsUUID(ludar.AnonymousAddress) { - merr.Append(errors.New("failed to validate 'anonymousAddress': must be uuid")) - } - if merr.Count() > 0 { - return merr - } - return nil -} - -// Decode - implementation of decodable interface -func (ludar *LinkUpholdDepositAccountRequest) Decode(ctx context.Context, v []byte) error { - if err := inputs.DecodeJSON(ctx, v, ludar); err != nil { - return fmt.Errorf("failed to decode json: %w", err) - } - return nil -} - -// HandleErrors - handle any errors from this request -func (ludar *LinkUpholdDepositAccountRequest) HandleErrors(err error) *handlers.AppError { - issues := map[string]string{} - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - if strings.Contains(e.Error(), "failed decoding") { - issues["decoding"] = e.Error() - } - if strings.Contains(e.Error(), "failed validation") { - issues["validation"] = e.Error() - } - } - } - return handlers.ValidationError("brave create wallet request validation errors", issues) -} - -// LinkBraveDepositAccountRequest - the structure for a linking request for uphold deposit account -type LinkBraveDepositAccountRequest struct { - DepositDestination string `json:"depositDestination"` -} - -// Validate - implementation of validatable interface -func (lbdar *LinkBraveDepositAccountRequest) Validate(ctx context.Context) error { - var merr = new(errorutils.MultiError) - if lbdar.DepositDestination != "" && !govalidator.IsUUID(lbdar.DepositDestination) { - merr.Append(errors.New("failed to validate 'depositDestination': must be uuid")) - } - if merr.Count() > 0 { - return merr - } - return nil -} - -// Decode - implementation of decodable interface -func (lbdar *LinkBraveDepositAccountRequest) Decode(ctx context.Context, v []byte) error { - if err := inputs.DecodeJSON(ctx, v, lbdar); err != nil { - return fmt.Errorf("failed to decode json: %w", err) - } - return nil -} - -// HandleErrors - handle any errors from this request -func (lbdar *LinkBraveDepositAccountRequest) HandleErrors(err error) *handlers.AppError { - issues := map[string]string{} - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - if strings.Contains(e.Error(), "failed decoding") { - issues["decoding"] = e.Error() - } - if strings.Contains(e.Error(), "failed validation") { - issues["validation"] = e.Error() - } - } - } - return handlers.ValidationError("brave link wallet request validation errors", issues) -} - -// ZebPayLinkingRequest holds info needed to link zebpay account. -type ZebPayLinkingRequest struct { - VerificationToken string `json:"linking_info"` -} - -// Validate implements DecodeValidate interface. -func (r *ZebPayLinkingRequest) Validate(ctx context.Context) error { - if r.VerificationToken == "" { - return ErrZebPayInvalidVrfToken - } - - return nil -} - -// Decode implements DecodeValidate interface. -func (r *ZebPayLinkingRequest) Decode(ctx context.Context, v []byte) error { - if err := inputs.DecodeJSON(ctx, v, r); err != nil { - return fmt.Errorf("failed to decode json: %w", err) - } - - return nil -} - -// HandleErrorsZebPay returns an AppError for the given err. -func HandleErrorsZebPay(err error) *handlers.AppError { - - // all other errors are 400s - issues := make(map[string]string) - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - msg := e.Error() - - if strings.Contains(msg, "failed decoding") { - issues["decoding"] = msg - continue - } - - if strings.Contains(msg, "failed validation") { - issues["validation"] = msg - continue - } - } - } - - return handlers.ValidationError("zebpay wallet linking request validation errors", issues) -} - -// GeminiLinkingRequest holds info needed to link gemini account -type GeminiLinkingRequest struct { - VerificationToken string `json:"linking_info"` - DepositID string `json:"recipient_id"` -} - -// Validate - implementation of validatable interface -func (glr *GeminiLinkingRequest) Validate(ctx context.Context) error { - if glr.VerificationToken == "" { - return errors.New("failed to validate 'linking_info': must not be empty") - } - return nil -} - -// Decode - implementation of decodable interface -func (glr *GeminiLinkingRequest) Decode(ctx context.Context, v []byte) error { - if err := inputs.DecodeJSON(ctx, v, glr); err != nil { - return fmt.Errorf("failed to decode json: %w", err) - } - return nil -} - -// HandleErrors - handle any errors from this request -func (glr *GeminiLinkingRequest) HandleErrors(err error) *handlers.AppError { - issues := map[string]string{} - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - if strings.Contains(e.Error(), "failed decoding") { - issues["decoding"] = e.Error() - } - if strings.Contains(e.Error(), "failed validation") { - issues["validation"] = e.Error() - } - } - } - return handlers.ValidationError("gemini wallet linking request validation errors", issues) -} - -// BitFlyerLinkingRequest - the structure for a brave provider wallet creation request -type BitFlyerLinkingRequest struct { - LinkingInfo string `json:"linkingInfo"` - DepositID string `json:"-"` - AccountHash string `json:"-"` -} - -// BitFlyerLinkingInfo - jwt structure of the linking info -type BitFlyerLinkingInfo struct { - DepositID string `json:"deposit_id"` - RequestID string `json:"request_id"` - AccountHash string `json:"account_hash"` - ExternalAccountID string `json:"external_account_id"` - Timestamp time.Time `json:"timestamp"` -} - -// Validate - implementation of validatable interface -func (blr *BitFlyerLinkingRequest) Validate(ctx context.Context) error { - // validate there is a signed creation request - if blr.LinkingInfo == "" { - return ErrMissingSignedLinkingRequest - } - - // get the bitflyer jwt key from ctx - jwtKey, err := appctx.GetByteSliceFromContext(ctx, appctx.BitFlyerJWTKeyCTXKey) - if err != nil { - return fmt.Errorf("configuration error, no jwt validation key: %w", err) - } - - tok, err := jwt.ParseSigned(blr.LinkingInfo) - if err != nil { - return fmt.Errorf("failed to parse the linking info jwt token: %w", err) - } - - base := jwt.Claims{} - linkingInfo := BitFlyerLinkingInfo{} - - if err := tok.Claims(jwtKey, &base, &linkingInfo); err != nil { - return fmt.Errorf("failed to parse the linking info jwt token: %w", err) - } - - // Linking Info token is not to be more than 2 minutes old - if time.Since(linkingInfo.Timestamp) > 2*time.Minute { - return fmt.Errorf("failed to validate token, timestamp is over 2 minutes old") - } - - // ExternalAccountID is Hex encoded sha256 digest of the payment id - // 1.) grab the payment id string from the context (comes from http signature) - paymentID, err := middleware.GetKeyID(ctx) - if err != nil { - return fmt.Errorf("failed to validate linking info jwt token, unable to key payment_id from signature: %w", err) - } - // 2.) sha256 digest - h := sha256.New() - if _, err := h.Write([]byte(paymentID)); err != nil { - return fmt.Errorf("failed to validate linking info jwt token, hash payment_id: %w", err) - } - // 3.) hex encode - // 4.) compare to external account id from linking info - hashed := hex.EncodeToString(h.Sum(nil)) - if !strings.EqualFold(hashed, linkingInfo.ExternalAccountID) { - return fmt.Errorf("failed to validate linking info jwt token, external account id invalid: %w", err) - } - - if bitFlyerRequestIDSpent(ctx, linkingInfo.RequestID) { - return fmt.Errorf("failed to validate linking info jwt token, request id already used: %w", err) - } - - blr.DepositID = linkingInfo.DepositID - blr.AccountHash = linkingInfo.AccountHash - - if blr.AccountHash == "" || blr.DepositID == "" { - // failed to extract claims, or the token is invalid - return fmt.Errorf("failed to parse claims: %w", err) - } - - return nil -} - -// Decode - implementation of decodable interface -func (blr *BitFlyerLinkingRequest) Decode(ctx context.Context, v []byte) error { - if err := inputs.DecodeJSON(ctx, v, blr); err != nil { - return fmt.Errorf("failed to decode json: %w", err) - } - - // TODO: pull out the DepositID and AccountHash from the JWT - // and set them in blr - return nil -} - -// HandleErrors - handle any errors from this request -func (blr *BitFlyerLinkingRequest) HandleErrors(err error) *handlers.AppError { - issues := map[string]string{} - if errors.Is(err, ErrInvalidJSON) { - issues["invalidJSON"] = err.Error() - } - - var merr *errorutils.MultiError - if errors.As(err, &merr) { - for _, e := range merr.Errs { - if strings.Contains(e.Error(), "failed decoding") { - issues["decoding"] = e.Error() - } - if strings.Contains(e.Error(), "failed validation") { - issues["validation"] = e.Error() - } - if errors.Is(e, ErrMissingLinkingInfo) { - issues["linkingInfo"] = "value is required" - } - } - } - return handlers.ValidationError("bitflyer deposit wallet linking request validation errors", issues) -} diff --git a/services/wallet/instrumented_datastore.go b/services/wallet/instrumented_datastore.go deleted file mode 100644 index 7bcf2e1f0..000000000 --- a/services/wallet/instrumented_datastore.go +++ /dev/null @@ -1,360 +0,0 @@ -package wallet - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/wallet -i Datastore -t ../../.prom-gowrap.tmpl -o instrumented_datastore.go -l "" - -import ( - "context" - "time" - - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/libs/clients/reputation" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - uuid "github.com/satori/go.uuid" -) - -// DatastoreWithPrometheus implements Datastore interface with all methods wrapped -// with Prometheus metrics -type DatastoreWithPrometheus struct { - base Datastore - instanceName string -} - -var datastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "wallet_datastore_duration_seconds", - Help: "datastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewDatastoreWithPrometheus returns an instance of the Datastore decorated with prometheus summary metric -func NewDatastoreWithPrometheus(base Datastore, instanceName string) DatastoreWithPrometheus { - return DatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// BeginTx implements Datastore -func (_d DatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// ConnectCustodialWallet implements Datastore -func (_d DatastoreWithPrometheus) ConnectCustodialWallet(ctx context.Context, cl *CustodianLink, depositDest string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "ConnectCustodialWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.ConnectCustodialWallet(ctx, cl, depositDest) -} - -// DisconnectCustodialWallet implements Datastore -func (_d DatastoreWithPrometheus) DisconnectCustodialWallet(ctx context.Context, walletID uuid.UUID) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "DisconnectCustodialWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.DisconnectCustodialWallet(ctx, walletID) -} - -// GetByProviderLinkingID implements Datastore -func (_d DatastoreWithPrometheus) GetByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (iap1 *[]walletutils.Info, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetByProviderLinkingID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetByProviderLinkingID(ctx, providerLinkingID) -} - -// GetCustodianLinkByWalletID implements Datastore -func (_d DatastoreWithPrometheus) GetCustodianLinkByWalletID(ctx context.Context, ID uuid.UUID) (cp1 *CustodianLink, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetCustodianLinkByWalletID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetCustodianLinkByWalletID(ctx, ID) -} - -// GetCustodianLinkCount implements Datastore -func (_d DatastoreWithPrometheus) GetCustodianLinkCount(ctx context.Context, linkingID uuid.UUID, custodian string) (i1 int, i2 int, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetCustodianLinkCount", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetCustodianLinkCount(ctx, linkingID, custodian) -} - -// GetLinkingLimitInfo implements Datastore -func (_d DatastoreWithPrometheus) GetLinkingLimitInfo(ctx context.Context, providerLinkingID string) (m1 map[string]LinkingInfo, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetLinkingLimitInfo", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetLinkingLimitInfo(ctx, providerLinkingID) -} - -// GetLinkingsByProviderLinkingID implements Datastore -func (_d DatastoreWithPrometheus) GetLinkingsByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (la1 []LinkingMetadata, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetLinkingsByProviderLinkingID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetLinkingsByProviderLinkingID(ctx, providerLinkingID) -} - -// GetWallet implements Datastore -func (_d DatastoreWithPrometheus) GetWallet(ctx context.Context, ID uuid.UUID) (ip1 *walletutils.Info, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetWallet(ctx, ID) -} - -// GetWalletByPublicKey implements Datastore -func (_d DatastoreWithPrometheus) GetWalletByPublicKey(ctx context.Context, s1 string) (ip1 *walletutils.Info, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetWalletByPublicKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetWalletByPublicKey(ctx, s1) -} - -// HasPriorLinking implements Datastore -func (_d DatastoreWithPrometheus) HasPriorLinking(ctx context.Context, walletID uuid.UUID, providerLinkingID uuid.UUID) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "HasPriorLinking", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.HasPriorLinking(ctx, walletID, providerLinkingID) -} - -// InsertBitFlyerRequestID implements Datastore -func (_d DatastoreWithPrometheus) InsertBitFlyerRequestID(ctx context.Context, requestID string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertBitFlyerRequestID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertBitFlyerRequestID(ctx, requestID) -} - -// InsertVerifiedWalletOutboxTx implements Datastore -func (_d DatastoreWithPrometheus) InsertVerifiedWalletOutboxTx(ctx context.Context, tx *sqlx.Tx, paymentID uuid.UUID, verifiedWallet bool) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertVerifiedWalletOutboxTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertVerifiedWalletOutboxTx(ctx, tx, paymentID, verifiedWallet) -} - -// InsertWallet implements Datastore -func (_d DatastoreWithPrometheus) InsertWallet(ctx context.Context, wallet *walletutils.Info) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertWallet(ctx, wallet) -} - -// InsertWalletTx implements Datastore -func (_d DatastoreWithPrometheus) InsertWalletTx(ctx context.Context, tx *sqlx.Tx, wallet *walletutils.Info) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "InsertWalletTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.InsertWalletTx(ctx, tx, wallet) -} - -// LinkWallet implements Datastore -func (_d DatastoreWithPrometheus) LinkWallet(ctx context.Context, id string, providerID string, providerLinkingID uuid.UUID, depositProvider string, country string) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "LinkWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.LinkWallet(ctx, id, providerID, providerLinkingID, depositProvider, country) -} - -// Migrate implements Datastore -func (_d DatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements Datastore -func (_d DatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements Datastore -func (_d DatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements Datastore -func (_d DatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements Datastore -func (_d DatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} - -// SendVerifiedWalletOutbox implements Datastore -func (_d DatastoreWithPrometheus) SendVerifiedWalletOutbox(ctx context.Context, client reputation.Client, retry backoff.RetryFunc) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "SendVerifiedWalletOutbox", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.SendVerifiedWalletOutbox(ctx, client, retry) -} - -// UpsertWallet implements Datastore -func (_d DatastoreWithPrometheus) UpsertWallet(ctx context.Context, wallet *walletutils.Info) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "UpsertWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.UpsertWallet(ctx, wallet) -} diff --git a/services/wallet/instrumented_read_only_datastore.go b/services/wallet/instrumented_read_only_datastore.go deleted file mode 100755 index f161f6976..000000000 --- a/services/wallet/instrumented_read_only_datastore.go +++ /dev/null @@ -1,204 +0,0 @@ -package wallet - -// Code generated by gowrap. DO NOT EDIT. -// template: ../../.prom-gowrap.tmpl -// gowrap: http://github.com/hexdigest/gowrap - -//go:generate gowrap gen -p github.com/brave-intl/bat-go/services/wallet -i ReadOnlyDatastore -t ../../.prom-gowrap.tmpl -o instrumented_read_only_datastore.go -l "" - -import ( - "context" - "time" - - walletutils "github.com/brave-intl/bat-go/libs/wallet" - migrate "github.com/golang-migrate/migrate/v4" - "github.com/jmoiron/sqlx" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - uuid "github.com/satori/go.uuid" -) - -// ReadOnlyDatastoreWithPrometheus implements ReadOnlyDatastore interface with all methods wrapped -// with Prometheus metrics -type ReadOnlyDatastoreWithPrometheus struct { - base ReadOnlyDatastore - instanceName string -} - -var readonlydatastoreDurationSummaryVec = promauto.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "wallet_readonly_datastore_duration_seconds", - Help: "readonlydatastore runtime duration and result", - MaxAge: time.Minute, - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"instance_name", "method", "result"}) - -// NewReadOnlyDatastoreWithPrometheus returns an instance of the ReadOnlyDatastore decorated with prometheus summary metric -func NewReadOnlyDatastoreWithPrometheus(base ReadOnlyDatastore, instanceName string) ReadOnlyDatastoreWithPrometheus { - return ReadOnlyDatastoreWithPrometheus{ - base: base, - instanceName: instanceName, - } -} - -// BeginTx implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) BeginTx() (tp1 *sqlx.Tx, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "BeginTx", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.BeginTx() -} - -// GetByProviderLinkingID implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (iap1 *[]walletutils.Info, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetByProviderLinkingID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetByProviderLinkingID(ctx, providerLinkingID) -} - -// GetCustodianLinkCount implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetCustodianLinkCount(ctx context.Context, linkingID uuid.UUID, custodian string) (i1 int, i2 int, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetCustodianLinkCount", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetCustodianLinkCount(ctx, linkingID, custodian) -} - -// GetLinkingsByProviderLinkingID implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetLinkingsByProviderLinkingID(ctx context.Context, providerLinkingID uuid.UUID) (la1 []LinkingMetadata, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetLinkingsByProviderLinkingID", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetLinkingsByProviderLinkingID(ctx, providerLinkingID) -} - -// GetWallet implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetWallet(ctx context.Context, ID uuid.UUID) (ip1 *walletutils.Info, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetWallet", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetWallet(ctx, ID) -} - -// GetWalletByPublicKey implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) GetWalletByPublicKey(ctx context.Context, s1 string) (ip1 *walletutils.Info, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetWalletByPublicKey", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.GetWalletByPublicKey(ctx, s1) -} - -// HasPriorLinking implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) HasPriorLinking(ctx context.Context, walletID uuid.UUID, providerLinkingID uuid.UUID) (b1 bool, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "HasPriorLinking", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.HasPriorLinking(ctx, walletID, providerLinkingID) -} - -// Migrate implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) Migrate(p1 ...uint) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "Migrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.Migrate(p1...) -} - -// NewMigrate implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) NewMigrate() (mp1 *migrate.Migrate, err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "NewMigrate", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.NewMigrate() -} - -// RawDB implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RawDB() (dp1 *sqlx.DB) { - _since := time.Now() - defer func() { - result := "ok" - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RawDB", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RawDB() -} - -// RollbackTx implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RollbackTx(tx *sqlx.Tx) { - _since := time.Now() - defer func() { - result := "ok" - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTx", result).Observe(time.Since(_since).Seconds()) - }() - _d.base.RollbackTx(tx) - return -} - -// RollbackTxAndHandle implements ReadOnlyDatastore -func (_d ReadOnlyDatastoreWithPrometheus) RollbackTxAndHandle(tx *sqlx.Tx) (err error) { - _since := time.Now() - defer func() { - result := "ok" - if err != nil { - result = "error" - } - - readonlydatastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "RollbackTxAndHandle", result).Observe(time.Since(_since).Seconds()) - }() - return _d.base.RollbackTxAndHandle(tx) -} diff --git a/services/wallet/keystore.go b/services/wallet/keystore.go deleted file mode 100644 index 013c90d3b..000000000 --- a/services/wallet/keystore.go +++ /dev/null @@ -1,59 +0,0 @@ -package wallet - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/httpsignature" - uuid "github.com/satori/go.uuid" -) - -// LookupVerifier based on the HTTP signing keyID, which in our case is the walletID -func (service *Service) LookupVerifier(ctx context.Context, keyID string) (context.Context, *httpsignature.Verifier, error) { - walletID, err := uuid.FromString(keyID) - if err != nil { - return nil, nil, errorutils.Wrap(err, "KeyID format is invalid") - } - - wallet, err := service.GetWallet(ctx, walletID) - if err != nil { - return nil, nil, errorutils.Wrap(err, "error getting wallet") - } - - if wallet == nil { - return nil, nil, nil - } - - var publicKey httpsignature.Ed25519PubKey - if len(wallet.PublicKey) > 0 { - var err error - publicKey, err = hex.DecodeString(wallet.PublicKey) - if err != nil { - return nil, nil, err - } - } - tmp := httpsignature.Verifier(publicKey) - return ctx, &tmp, nil -} - -// DecodeEd25519Keystore is a keystore that "looks up" a verifier by attempting to decode the keyID as a base64 encoded ed25519 public key -type DecodeEd25519Keystore struct{} - -// LookupVerifier by decoding keyID -func (d *DecodeEd25519Keystore) LookupVerifier(ctx context.Context, keyID string) (context.Context, *httpsignature.Verifier, error) { - var publicKey httpsignature.Ed25519PubKey - if len(keyID) > 0 { - var err error - publicKey, err = hex.DecodeString(keyID) - if err != nil { - return nil, nil, fmt.Errorf("failed to hex decode public key: %w", err) - } - } else { - return nil, nil, errors.New("empty KeyId is not valid") - } - verifier := httpsignature.Verifier(publicKey) - return ctx, &verifier, nil -} diff --git a/services/wallet/keystore_test.go b/services/wallet/keystore_test.go deleted file mode 100644 index eef23498c..000000000 --- a/services/wallet/keystore_test.go +++ /dev/null @@ -1,530 +0,0 @@ -//go:build integration - -package wallet_test - -import ( - "bytes" - "context" - "crypto" - "crypto/ed25519" - "encoding/hex" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/brave-intl/bat-go/libs/altcurrency" - mock_reputation "github.com/brave-intl/bat-go/libs/clients/reputation/mock" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/httpsignature" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/brave-intl/bat-go/services/wallet" - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type WalletControllersTestSuite struct { - suite.Suite -} - -func TestWalletControllersTestSuite(t *testing.T) { - suite.Run(t, new(WalletControllersTestSuite)) -} - -func (suite *WalletControllersTestSuite) SetupSuite() { - pg, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - m, err := pg.NewMigrate() - suite.Require().NoError(err, "Failed to create migrate instance") - - ver, dirty, _ := m.Version() - if dirty { - suite.Require().NoError(m.Force(int(ver))) - } - if ver > 0 { - suite.Require().NoError(m.Down(), "Failed to migrate down cleanly") - } - - suite.Require().NoError(pg.Migrate(), "Failed to fully migrate") -} - -func (suite *WalletControllersTestSuite) SetupTest() { - suite.CleanDB() -} - -func (suite *WalletControllersTestSuite) TearDownTest() { - suite.CleanDB() -} - -func (suite *WalletControllersTestSuite) CleanDB() { - tables := []string{"claim_creds", "claims", "wallets", "issuers", "promotions", "wallet_custodian"} - - pg, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres conn") - - for _, table := range tables { - _, err = pg.RawDB().Exec("delete from " + table) - suite.Require().NoError(err, "Failed to get clean table") - } -} - -func noUUID() *uuid.UUID { - return nil -} - -func (suite *WalletControllersTestSuite) FundWallet(w *uphold.Wallet, probi decimal.Decimal) decimal.Decimal { - ctx := context.Background() - balanceBefore, err := w.GetBalance(ctx, true) - total, err := uphold.FundWallet(ctx, w, probi) - suite.Require().NoError(err, "an error should not be generated from funding the wallet") - suite.Require().True(total.GreaterThan(balanceBefore.TotalProbi), "submit with confirm should result in an increased balance") - return total -} - -func (suite *WalletControllersTestSuite) CheckBalance(w *uphold.Wallet, expect decimal.Decimal) { - balances, err := w.GetBalance(context.Background(), true) - suite.Require().NoError(err, "an error should not be generated from checking the wallet balance") - totalProbi := altcurrency.BAT.FromProbi(balances.TotalProbi) - errMessage := fmt.Sprintf("got an unexpected balance. expected: %s, got %s", expect.String(), totalProbi.String()) - suite.Require().True(expect.Equal(totalProbi), errMessage) -} - -func (suite *WalletControllersTestSuite) TestBalanceV3() { - pg, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres connection") - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - service, _ := wallet.InitService(pg, nil, nil, nil, nil, nil, nil, nil) - - w1 := suite.NewWallet(service, "uphold") - - bat1 := decimal.NewFromFloat(0.000000001) - - suite.FundWallet(w1, bat1) - - // check there is 1 bat in w1 - suite.CheckBalance(w1, bat1) - - // call the balance endpoint and check that you get back a total of 1 - handler := wallet.GetUpholdWalletBalanceV3 - - req, err := http.NewRequest("GET", "/v3/wallet/uphold/{paymentID}", nil) - suite.Require().NoError(err, "wallet claim request could not be created") - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("paymentID", w1.ID) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - req = req.WithContext(context.WithValue(req.Context(), appctx.RODatastoreCTXKey, pg)) - req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) - - rr := httptest.NewRecorder() - handlers.AppHandler(handler).ServeHTTP(rr, req) - suite.Require().Equal(http.StatusOK, rr.Code, fmt.Sprintf("status is expected to match %d: %s", http.StatusOK, rr.Body.String())) - - var balance wallet.BalanceResponseV3 - err = json.Unmarshal(rr.Body.Bytes(), &balance) - suite.Require().NoError(err, "failed to unmarshal balance result") - - suite.Require().Equal(balance.Total, float64(0.000000001), fmt.Sprintf("balance is expected to match %f: %f", balance.Total, float64(1))) - - _, err = pg.RawDB().Exec(`update wallets set provider_id = '' where id = $1`, w1.ID) - suite.Require().NoError(err, "wallet provider_id could not be set as empty string") - - req, err = http.NewRequest("GET", "/v3/wallet/uphold/{paymentID}", nil) - suite.Require().NoError(err, "wallet claim request could not be created") - - rctx = chi.NewRouteContext() - rctx.URLParams.Add("paymentID", w1.ID) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - req = req.WithContext(context.WithValue(req.Context(), appctx.RODatastoreCTXKey, pg)) - req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) - - rr = httptest.NewRecorder() - handlers.AppHandler(handler).ServeHTTP(rr, req) - expectingForbidden := fmt.Sprintf("status is expected to match %d: %s", http.StatusForbidden, rr.Body.String()) - suite.Require().Equal(http.StatusForbidden, rr.Code, expectingForbidden) -} - -func (suite *WalletControllersTestSuite) TestLinkWalletV3() { - ctx := context.Background() - - pg, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres connection") - - mockCtrl := gomock.NewController(suite.T()) - defer mockCtrl.Finish() - - service, _ := wallet.InitService(pg, nil, nil, nil, nil, nil, nil, nil) - - w1 := suite.NewWallet(service, "uphold") - w2 := suite.NewWallet(service, "uphold") - w3 := suite.NewWallet(service, "uphold") - w4 := suite.NewWallet(service, "uphold") - bat1 := decimal.NewFromFloat(0.000000001) - bat2 := decimal.NewFromFloat(0.000000002) - - suite.FundWallet(w1, bat1) - suite.FundWallet(w2, bat1) - suite.FundWallet(w3, bat1) - suite.FundWallet(w4, bat1) - - anonCard1ID, err := w1.CreateCardAddress(ctx, "anonymous") - suite.Require().NoError(err, "create anon card must not fail") - anonCard1UUID := uuid.Must(uuid.FromString(anonCard1ID)) - - anonCard2ID, err := w2.CreateCardAddress(ctx, "anonymous") - suite.Require().NoError(err, "create anon card must not fail") - anonCard2UUID := uuid.Must(uuid.FromString(anonCard2ID)) - - anonCard3ID, err := w3.CreateCardAddress(ctx, "anonymous") - suite.Require().NoError(err, "create anon card must not fail") - anonCard3UUID := uuid.Must(uuid.FromString(anonCard3ID)) - - w1ProviderID := w1.GetWalletInfo().ProviderID - w2ProviderID := w2.GetWalletInfo().ProviderID - w3ProviderID := w3.GetWalletInfo().ProviderID - - zero := decimal.NewFromFloat(0) - - suite.CheckBalance(w1, bat1) - suite.claimCardV3(service, w1, w3ProviderID, http.StatusOK, bat1, &anonCard3UUID) - suite.CheckBalance(w1, zero) - - suite.CheckBalance(w2, bat1) - suite.claimCardV3(service, w2, w1ProviderID, http.StatusOK, zero, &anonCard1UUID) - suite.CheckBalance(w2, bat1) - - suite.CheckBalance(w2, bat1) - suite.claimCardV3(service, w2, w1ProviderID, http.StatusOK, bat1, &anonCard3UUID) - suite.CheckBalance(w2, zero) - - suite.CheckBalance(w3, bat2) - suite.claimCardV3(service, w3, w2ProviderID, http.StatusOK, bat1, &anonCard3UUID) - suite.CheckBalance(w3, bat1) - - suite.CheckBalance(w3, bat1) - suite.claimCardV3(service, w3, w1ProviderID, http.StatusOK, zero, &anonCard2UUID) - suite.CheckBalance(w3, bat1) -} - -func (suite *WalletControllersTestSuite) claimCardV3( - service *wallet.Service, - w *uphold.Wallet, - destination string, - status int, - amount decimal.Decimal, - anonymousAddress *uuid.UUID, -) (*walletutils.Info, string) { - signedCreationRequest, err := w.PrepareTransaction(*w.AltCurrency, altcurrency.BAT.ToProbi(amount), destination, "", "", nil) - - suite.Require().NoError(err, "transaction must be signed client side") - - // V3 Payload - reqBody := wallet.LinkUpholdDepositAccountRequest{ - SignedLinkingRequest: signedCreationRequest, - } - - if anonymousAddress != nil { - reqBody.AnonymousAddress = anonymousAddress.String() - } - - body, err := json.Marshal(&reqBody) - suite.Require().NoError(err, "unable to marshal claim body") - - info := w.GetWalletInfo() - - // V3 Handler - - handler := wallet.LinkUpholdDepositAccountV3(service) - - req, err := http.NewRequest("POST", "/v3/wallet/{paymentID}/claim", bytes.NewBuffer(body)) - suite.Require().NoError(err, "wallet claim request could not be created") - - ctrl := gomock.NewController(suite.T()) - defer ctrl.Finish() - - repClient := mock_reputation.NewMockClient(ctrl) - repClient.EXPECT().IsLinkingReputable(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - - rctx := chi.NewRouteContext() - rctx.URLParams.Add("paymentID", info.ID) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) - req = req.WithContext(context.WithValue(req.Context(), appctx.ReputationClientCTXKey, repClient)) - - rr := httptest.NewRecorder() - handlers.AppHandler(handler).ServeHTTP(rr, req) - suite.Require().Equal(status, rr.Code, fmt.Sprintf("status is expected to match %d: %s", status, rr.Body.String())) - linked, err := service.Datastore.GetWallet(req.Context(), uuid.Must(uuid.FromString(w.ID))) - suite.Require().NoError(err, "retrieving the wallet did not cause an error") - return linked, rr.Body.String() -} - -func (suite *WalletControllersTestSuite) createBody( - tx string, -) string { - reqBody, _ := json.Marshal(wallet.UpholdCreationRequest{ - SignedCreationRequest: tx, - }) - return string(reqBody) -} - -func (suite *WalletControllersTestSuite) NewWallet(service *wallet.Service, provider string) *uphold.Wallet { - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - publicKeyString := hex.EncodeToString(publicKey) - - bat := altcurrency.BAT - info := walletutils.Info{ - ID: uuid.NewV4().String(), - PublicKey: publicKeyString, - Provider: provider, - AltCurrency: &bat, - } - w := &uphold.Wallet{ - Info: info, - PrivKey: privKey, - PubKey: publicKey, - } - - reg, err := w.PrepareRegistration("Brave Browser Test Link") - suite.Require().NoError(err, "unable to prepare transaction") - - createResp := suite.createUpholdWalletV3( - service, - suite.createBody(reg), - http.StatusCreated, - publicKey, - privKey, - true, - ) - - var returnedInfo wallet.ResponseV3 - err = json.Unmarshal([]byte(createResp), &returnedInfo) - suite.Require().NoError(err, "unable to create wallet") - convertedInfo := wallet.ResponseV3ToInfo(returnedInfo) - w.Info = *convertedInfo - return w -} - -func (suite *WalletControllersTestSuite) TestCreateBraveWalletV3() { - pg, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres connection") - - service, _ := wallet.InitService(pg, nil, nil, nil, nil, nil, nil, nil) - - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - - // assume 400 is already covered - // fail because of lacking signature presence - notSignedResponse := suite.createUpholdWalletV3( - service, - `{}`, - http.StatusBadRequest, - publicKey, - privKey, - false, - ) - - suite.Assert().JSONEq(`{"code":400, "data":{"validationErrors":{"decoding":"failed decoding: failed to decode signed creation request: unexpected end of JSON input", "signedCreationRequest":"value is required", "validation":"failed validation: missing signed creation request"}}, "message":"Error validating uphold create wallet request validation errors"}`, notSignedResponse, "field is not valid") - - createResp := suite.createBraveWalletV3( - service, - ``, - http.StatusCreated, - publicKey, - privKey, - true, - ) - - var created wallet.ResponseV3 - err = json.Unmarshal([]byte(createResp), &created) - suite.Require().NoError(err, "unable to unmarshal response") - - getResp := suite.getWallet(service, uuid.Must(uuid.FromString(created.PaymentID)), http.StatusOK) - - var gotten wallet.ResponseV3 - err = json.Unmarshal([]byte(getResp), &gotten) - suite.Require().NoError(err, "unable to unmarshal response") - // does not return wallet provider - suite.Require().Equal(created, gotten, "the get and create return the same structure") -} - -func (suite *WalletControllersTestSuite) TestCreateUpholdWalletV3() { - pg, _, err := wallet.NewPostgres() - suite.Require().NoError(err, "Failed to get postgres connection") - - service, _ := wallet.InitService(pg, nil, nil, nil, nil, nil, nil, nil) - - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - - badJSONBodyParse := suite.createUpholdWalletV3( - service, - ``, - http.StatusBadRequest, - publicKey, - privKey, - true, - ) - suite.Assert().JSONEq(`{ - "code":400, - "data": { - "validationErrors":{ - "decoding":"failed decoding: failed to decode json: EOF", - "signedCreationRequest":"value is required", - "validation":"failed validation: missing signed creation request" - } - }, - "message":"Error validating uphold create wallet request validation errors" - }`, badJSONBodyParse, "should fail when parsing json") - - badFieldResponse := suite.createUpholdWalletV3( - service, - `{"signedCreationRequest":""}`, - http.StatusBadRequest, - publicKey, - privKey, - true, - ) - - suite.Assert().JSONEq(`{ - "code":400, "data":{"validationErrors":{"decoding":"failed decoding: failed to decode signed creation request: unexpected end of JSON input", "signedCreationRequest":"value is required", "validation":"failed validation: missing signed creation request"}}, "message":"Error validating uphold create wallet request validation errors"}`, badFieldResponse, "field is not valid") - - // assume 403 is already covered - // fail because of lacking signature presence - notSignedResponse := suite.createUpholdWalletV3( - service, - `{}`, - http.StatusBadRequest, - publicKey, - privKey, - false, - ) - - suite.Assert().JSONEq(`{ -"code":400, "data":{"validationErrors":{"decoding":"failed decoding: failed to decode signed creation request: unexpected end of JSON input", "signedCreationRequest":"value is required", "validation":"failed validation: missing signed creation request"}}, "message":"Error validating uphold create wallet request validation errors" - }`, notSignedResponse, "field is not valid") -} - -func (suite *WalletControllersTestSuite) getWallet( - service *wallet.Service, - paymentId uuid.UUID, - code int, -) string { - handler := handlers.AppHandler(wallet.GetWalletV3) - - req, err := http.NewRequest("GET", "/v3/wallet/"+paymentId.String(), nil) - suite.Require().NoError(err, "a request should be created") - - req = req.WithContext(context.WithValue(req.Context(), appctx.DatastoreCTXKey, service.Datastore)) - req = req.WithContext(context.WithValue(req.Context(), appctx.RODatastoreCTXKey, service.Datastore)) - req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) - rctx := chi.NewRouteContext() - rctx.URLParams.Add("paymentID", paymentId.String()) - req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - - suite.Require().Equal(code, rr.Code, "known status code should be sent: "+rr.Body.String()) - - return rr.Body.String() -} - -func (suite *WalletControllersTestSuite) createBraveWalletV3( - service *wallet.Service, - body string, - code int, - publicKey httpsignature.Ed25519PubKey, - privateKey ed25519.PrivateKey, - shouldSign bool, -) string { - - handler := handlers.AppHandler(wallet.CreateBraveWalletV3) - - bodyBuffer := bytes.NewBuffer([]byte(body)) - req, err := http.NewRequest("POST", "/v3/wallet/brave", bodyBuffer) - suite.Require().NoError(err, "a request should be created") - - // setup context - req = req.WithContext(context.WithValue(context.Background(), appctx.DatastoreCTXKey, service.Datastore)) - req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) - - if shouldSign { - suite.SignRequest( - req, - publicKey, - privateKey, - ) - } - - rctx := chi.NewRouteContext() - joined := context.WithValue(req.Context(), chi.RouteCtxKey, rctx) - req = req.WithContext(joined) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(code, rr.Code, "known status code should be sent: "+rr.Body.String()) - - return rr.Body.String() -} - -func (suite *WalletControllersTestSuite) createUpholdWalletV3( - service *wallet.Service, - body string, - code int, - publicKey httpsignature.Ed25519PubKey, - privateKey ed25519.PrivateKey, - shouldSign bool, -) string { - - handler := handlers.AppHandler(wallet.CreateUpholdWalletV3) - - bodyBuffer := bytes.NewBuffer([]byte(body)) - req, err := http.NewRequest("POST", "/v3/wallet", bodyBuffer) - suite.Require().NoError(err, "a request should be created") - - // setup context - req = req.WithContext(context.WithValue(context.Background(), appctx.DatastoreCTXKey, service.Datastore)) - req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) - - if shouldSign { - suite.SignRequest( - req, - publicKey, - privateKey, - ) - } - - rctx := chi.NewRouteContext() - joined := context.WithValue(req.Context(), chi.RouteCtxKey, rctx) - req = req.WithContext(joined) - - rr := httptest.NewRecorder() - handler.ServeHTTP(rr, req) - suite.Require().Equal(code, rr.Code, "known status code should be sent: "+rr.Body.String()) - - return rr.Body.String() -} - -func (suite *WalletControllersTestSuite) SignRequest( - req *http.Request, - publicKey httpsignature.Ed25519PubKey, - privateKey ed25519.PrivateKey, -) { - var s httpsignature.SignatureParams - s.Algorithm = httpsignature.ED25519 - s.KeyID = hex.EncodeToString(publicKey) - s.Headers = []string{"digest", "(request-target)"} - - err := s.Sign(privateKey, crypto.Hash(0), req) - suite.Require().NoError(err) -} diff --git a/services/wallet/metric/metric.go b/services/wallet/metric/metric.go deleted file mode 100644 index de09c8c86..000000000 --- a/services/wallet/metric/metric.go +++ /dev/null @@ -1,92 +0,0 @@ -package metric - -import ( - "github.com/brave-intl/bat-go/libs/clients/gemini" - "github.com/prometheus/client_golang/prometheus" -) - -const ( - status = "status" - countryCode = "country_code" - success = "success" - failure = "failure" -) - -type Metric struct { - cntLinkZP *prometheus.CounterVec - cntAccValidateGemini *prometheus.CounterVec - cntDocTypeByIssuingCntry *prometheus.CounterVec -} - -// New returns a new metric.Metric. -// New panics if it cannot register any of the metrics. -func New() *Metric { - clzp := prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "count_link_zebpay", - Help: "Counts the number of successful and failed ZebPay linkings partitioned by country code", - }, - []string{status, countryCode}, - ) - prometheus.MustRegister(clzp) - - accValidate := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "count_gemini_wallet_account_validation", - Help: "Counts the number of gemini wallets requesting account validation partitioned by country code", - }, - []string{countryCode, status}, - ) - prometheus.MustRegister(accValidate) - - cntDocTypeByIssuingCntry := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "count_gemini_document_type_by_issuing_country", - Help: "Counts the number document types being used for KYC broken down by country", - }, - []string{"document_type", "issuing_country"}, - ) - prometheus.MustRegister(cntDocTypeByIssuingCntry) - - return &Metric{ - cntLinkZP: clzp, - cntAccValidateGemini: accValidate, - cntDocTypeByIssuingCntry: cntDocTypeByIssuingCntry, - } -} - -func (m *Metric) LinkSuccessZP(cc string) { - m.cntLinkZP.With(prometheus.Labels{ - countryCode: cc, - status: success, - }).Inc() -} - -func (m *Metric) LinkFailureZP(cc string) { - m.cntLinkZP.With(prometheus.Labels{ - countryCode: cc, - status: failure, - }).Inc() -} - -func (m *Metric) LinkFailureGemini(cc string) { - m.cntAccValidateGemini.With(prometheus.Labels{ - countryCode: cc, - status: failure, - }).Inc() -} - -func (m *Metric) LinkSuccessGemini(cc string) { - m.cntAccValidateGemini.With(prometheus.Labels{ - countryCode: cc, - status: success, - }).Inc() -} - -func (m *Metric) CountDocTypeByIssuingCntry(validDocs []gemini.ValidDocument) { - for i := range validDocs { - m.cntDocTypeByIssuingCntry.With(prometheus.Labels{ - "document_type": validDocs[i].Type, - "issuing_country": validDocs[i].IssuingCountry, - }).Inc() - } -} diff --git a/services/wallet/mockservice.go b/services/wallet/mockservice.go deleted file mode 100644 index 56fb0791f..000000000 --- a/services/wallet/mockservice.go +++ /dev/null @@ -1,97 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./wallet/service.go - -// Package wallet is a generated GoMock package. -package wallet - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockGeoValidator is a mock of GeoValidator interface. -type MockGeoValidator struct { - ctrl *gomock.Controller - recorder *MockGeoValidatorMockRecorder -} - -// MockGeoValidatorMockRecorder is the mock recorder for MockGeoValidator. -type MockGeoValidatorMockRecorder struct { - mock *MockGeoValidator -} - -// NewMockGeoValidator creates a new mock instance. -func NewMockGeoValidator(ctrl *gomock.Controller) *MockGeoValidator { - mock := &MockGeoValidator{ctrl: ctrl} - mock.recorder = &MockGeoValidatorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockGeoValidator) EXPECT() *MockGeoValidatorMockRecorder { - return m.recorder -} - -// Validate mocks base method. -func (m *MockGeoValidator) Validate(ctx context.Context, geolocation string) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Validate", ctx, geolocation) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Validate indicates an expected call of Validate. -func (mr *MockGeoValidatorMockRecorder) Validate(ctx, geolocation interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockGeoValidator)(nil).Validate), ctx, geolocation) -} - -// MockmetricSvc is a mock of metricSvc interface. -type MockmetricSvc struct { - ctrl *gomock.Controller - recorder *MockmetricSvcMockRecorder -} - -// MockmetricSvcMockRecorder is the mock recorder for MockmetricSvc. -type MockmetricSvcMockRecorder struct { - mock *MockmetricSvc -} - -// NewMockmetricSvc creates a new mock instance. -func NewMockmetricSvc(ctrl *gomock.Controller) *MockmetricSvc { - mock := &MockmetricSvc{ctrl: ctrl} - mock.recorder = &MockmetricSvcMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockmetricSvc) EXPECT() *MockmetricSvcMockRecorder { - return m.recorder -} - -// LinkFailureZP mocks base method. -func (m *MockmetricSvc) LinkFailureZP(cc string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "LinkFailureZP", cc) -} - -// LinkFailureZP indicates an expected call of LinkFailureZP. -func (mr *MockmetricSvcMockRecorder) LinkFailureZP(cc interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkFailureZP", reflect.TypeOf((*MockmetricSvc)(nil).LinkFailureZP), cc) -} - -// LinkSuccessZP mocks base method. -func (m *MockmetricSvc) LinkSuccessZP(cc string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "LinkSuccessZP", cc) -} - -// LinkSuccessZP indicates an expected call of LinkSuccessZP. -func (mr *MockmetricSvcMockRecorder) LinkSuccessZP(cc interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSuccessZP", reflect.TypeOf((*MockmetricSvc)(nil).LinkSuccessZP), cc) -} diff --git a/services/wallet/model/model.go b/services/wallet/model/model.go deleted file mode 100644 index e4220bd8c..000000000 --- a/services/wallet/model/model.go +++ /dev/null @@ -1,11 +0,0 @@ -package model - -import "errors" - -var ErrNoWalletCustodian = errors.New("model: no linked wallet custodian") - -type Error string - -func (e Error) Error() string { - return string(e) -} diff --git a/services/wallet/outputs.go b/services/wallet/outputs.go deleted file mode 100644 index 48dde81a6..000000000 --- a/services/wallet/outputs.go +++ /dev/null @@ -1,187 +0,0 @@ -package wallet - -import ( - "github.com/brave-intl/bat-go/libs/altcurrency" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - uuid "github.com/satori/go.uuid" -) - -const ( - // InvalidCurrency - wallet currency is invalid - InvalidCurrency = "invalid" - // BATCurrency - wallet currency is BAT - BATCurrency = "BAT" - // BTCCurrency - wallet currency is BTC - BTCCurrency = "BTC" - // ETHCurrency - wallet currency is ETH - ETHCurrency = "ETH" - // LTCCurrency - wallet currency is LTC - LTCCurrency = "LTC" - - //UpholdProvider - provider label for uphold wallets - UpholdProvider = "uphold" - //BraveProvider - provider label for brave wallets - BraveProvider = "brave" -) - -// ProviderDetailsV3 - details about the provider -type ProviderDetailsV3 struct { - ID string `json:"id"` - Name string `json:"name"` - LinkingID string `json:"linkingId,omitempty"` - AnonymousAddress string `json:"anonymousAddress,omitempty"` -} - -// DepositAccountProviderDetailsV3 - details about the provider -type DepositAccountProviderDetailsV3 struct { - Name *string `json:"name"` - ID *string `json:"id"` - LinkingID string `json:"linkingId,omitempty"` - AnonymousAddress string `json:"anonymousAddress,omitempty"` -} - -// ResponseV3 - wallet creation response -type ResponseV3 struct { - PaymentID string `json:"paymentId"` - DepositAccountProvider *DepositAccountProviderDetailsV3 `json:"depositAccountProvider,omitempty"` - WalletProvider *ProviderDetailsV3 `json:"walletProvider,omitempty"` - AltCurrency string `json:"altcurrency"` - PublicKey string `json:"publicKey"` -} - -func convertAltCurrency(a *altcurrency.AltCurrency) string { - if a == nil { - return BATCurrency - } - switch *a { - case altcurrency.BAT: - return BATCurrency - case altcurrency.BTC: - return BTCCurrency - case altcurrency.ETH: - return ETHCurrency - case altcurrency.LTC: - return LTCCurrency - default: - return InvalidCurrency - } -} - -// ResponseV3ToInfo converts a response v3 to wallet info -func ResponseV3ToInfo(resp ResponseV3) *walletutils.Info { - alt, _ := altcurrency.FromString(resp.AltCurrency) - - // common to all wallet providers - info := walletutils.Info{ - ID: resp.PaymentID, - AltCurrency: &alt, - PublicKey: resp.PublicKey, - } - - if resp.WalletProvider != nil { - info.Provider = resp.WalletProvider.Name - if info.Provider == UpholdProvider { - // setup the anon card wallet information - info.ProviderID = resp.WalletProvider.ID - var providerLinkingID uuid.UUID - if resp.WalletProvider.LinkingID != "" { - providerLinkingID = uuid.Must(uuid.FromString(resp.WalletProvider.LinkingID)) - } - info.ProviderLinkingID = &providerLinkingID - - var anonymousAddress uuid.UUID - if resp.WalletProvider.AnonymousAddress != "" { - anonymousAddress = uuid.Must(uuid.FromString(resp.WalletProvider.AnonymousAddress)) - } - info.AnonymousAddress = &anonymousAddress - } - } - // setup the user deposit account info - depositAccountProvider := resp.DepositAccountProvider - if depositAccountProvider != nil { - info.UserDepositAccountProvider = depositAccountProvider.Name - providerLinkingID := uuid.Must(uuid.FromString(*depositAccountProvider.ID)) - info.ProviderLinkingID = &providerLinkingID - anonymousAddress := uuid.Must(uuid.FromString(depositAccountProvider.AnonymousAddress)) - info.AnonymousAddress = &anonymousAddress - } - return &info -} - -func infoToResponseV3(info *walletutils.Info) ResponseV3 { - var ( - linkingID string - anonymousAddress string - ) - if info == nil { - return ResponseV3{} - } - - var altCurrency = convertAltCurrency(info.AltCurrency) - - if info.ProviderLinkingID == nil { - linkingID = "" - } else { - linkingID = info.ProviderLinkingID.String() - } - - if info.AnonymousAddress == nil { - anonymousAddress = "" - } else { - anonymousAddress = info.AnonymousAddress.String() - } - - // common to all wallets - resp := ResponseV3{ - PaymentID: info.ID, - AltCurrency: altCurrency, - PublicKey: info.PublicKey, - WalletProvider: &ProviderDetailsV3{ - Name: info.Provider, - }, - } - - // setup the wallet provider (anon card uphold) - if info.Provider == "uphold" { - // this is a uphold provided wallet (anon card based) - resp.WalletProvider.ID = info.ProviderID - resp.WalletProvider.AnonymousAddress = anonymousAddress - resp.WalletProvider.LinkingID = linkingID - } - - // now setup user deposit account - if info.UserDepositAccountProvider != nil { - // this brave wallet has a linked deposit account - resp.DepositAccountProvider = &DepositAccountProviderDetailsV3{ - Name: info.UserDepositAccountProvider, - ID: &info.UserDepositDestination, - LinkingID: linkingID, - AnonymousAddress: anonymousAddress, - } - } - - return resp -} - -// BalanceResponseV3 - wallet creation response -type BalanceResponseV3 struct { - Total float64 `json:"total"` - Spendable float64 `json:"spendable"` - Confirmed float64 `json:"confirmed"` - Unconfirmed float64 `json:"unconfirmed"` -} - -func balanceToResponseV3(b walletutils.Balance) BalanceResponseV3 { - // convert to double, don't care about rounding - total, _ := altcurrency.BAT.FromProbi(b.TotalProbi).Float64() - spendable, _ := altcurrency.BAT.FromProbi(b.SpendableProbi).Float64() - confirmed, _ := altcurrency.BAT.FromProbi(b.ConfirmedProbi).Float64() - unconfirmed, _ := altcurrency.BAT.FromProbi(b.UnconfirmedProbi).Float64() - - return BalanceResponseV3{ - Total: total, - Spendable: spendable, - Confirmed: confirmed, - Unconfirmed: unconfirmed, - } -} diff --git a/services/wallet/service.go b/services/wallet/service.go deleted file mode 100644 index eef9c1817..000000000 --- a/services/wallet/service.go +++ /dev/null @@ -1,942 +0,0 @@ -package wallet - -import ( - "context" - "database/sql" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "net/http" - "os" - "strconv" - "strings" - "sync" - "time" - - "github.com/brave-intl/bat-go/services/wallet/metric" - "github.com/brave-intl/bat-go/services/wallet/model" - "github.com/go-chi/chi" - "github.com/go-jose/go-jose/v3/jwt" - "github.com/lib/pq" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/spf13/viper" - - "github.com/brave-intl/bat-go/libs/altcurrency" - appaws "github.com/brave-intl/bat-go/libs/aws" - "github.com/brave-intl/bat-go/libs/backoff" - "github.com/brave-intl/bat-go/libs/backoff/retrypolicy" - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/gemini" - "github.com/brave-intl/bat-go/libs/clients/reputation" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/middleware" - srv "github.com/brave-intl/bat-go/libs/service" - walletutils "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/brave-intl/bat-go/services/cmd" -) - -// VerifiedWalletEnable enable verified wallet call -var VerifiedWalletEnable = isVerifiedWalletEnable() - -func isVerifiedWalletEnable() bool { - var toggle = false - if os.Getenv("VERIFIED_WALLET_ENABLED") != "" { - var err error - toggle, err = strconv.ParseBool(os.Getenv("VERIFIED_WALLET_ENABLED")) - if err != nil { - return false - } - } - return toggle -} - -// directVerifiedWalletEnable enable direct verified wallet call -var directVerifiedWalletEnable = isDirectVerifiedWalletEnable() - -func isDirectVerifiedWalletEnable() bool { - var toggle = false - if os.Getenv("DIRECT_VERIFIED_WALLET_ENABLED") != "" { - var err error - toggle, err = strconv.ParseBool(os.Getenv("DIRECT_VERIFIED_WALLET_ENABLED")) - if err != nil { - return false - } - } - return toggle -} - -var ( - // ClaimNamespace uuidv5 namespace for provider linking - exported for tests - ClaimNamespace = uuid.Must(uuid.FromString("c39b298b-b625-42e9-a463-69c7726e5ddc")) -) - -var ( - retryPolicy = retrypolicy.DefaultRetry - nonRetriableErrors = []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, - http.StatusInternalServerError, http.StatusConflict} -) - -var ( - errGeoCountryDisabled = errors.New("geo country is disabled") - errRewardsWalletAlreadyExists = errors.New("rewards wallet already exists") - - errZPInvalidIat = errors.New("zebpay: linking info validation failed no iat") - errZPInvalidExp = errors.New("zebpay: linking info validation failed no exp") - errZPInvalidAfter = errors.New("zebpay: linking info validation failed issued at is after now") - errZPInvalidBefore = errors.New("zebpay: linking info validation failed expired is before now") - errZPInvalidKYC = errors.New("zebpay: user kyc did not pass") - errZPInvalidDepositID = errors.New("zebpay: deposit id does not match token") - errZPInvalidAccountID = errors.New("zebpay: account id invalid in token") - - errCustodianLinkMismatch = errors.New("wallet: custodian link mismatch") -) - -// GeoValidator - interface describing validation of geolocation -type GeoValidator interface { - Validate(ctx context.Context, geolocation string) (bool, error) -} - -type metricSvc interface { - LinkSuccessZP(cc string) - LinkFailureZP(cc string) - LinkFailureGemini(cc string) - LinkSuccessGemini(cc string) - CountDocTypeByIssuingCntry(validDocs []gemini.ValidDocument) -} - -type geminiSvc interface { - GetIssuingCountry(acc gemini.ValidatedAccount, fallback bool) string - IsRegionAvailable(ctx context.Context, issuingCountry string, custodianRegions custodian.Regions) error -} - -// Service contains datastore connections -type Service struct { - Datastore Datastore - RoDatastore ReadOnlyDatastore - repClient reputation.Client - geminiClient gemini.Client - geoValidator GeoValidator - retry backoff.RetryFunc - jobs []srv.Job - crMu *sync.RWMutex - custodianRegions custodian.Regions - metric metricSvc - gemini geminiSvc -} - -// InitService creates a service using the passed datastore and clients configured from the environment -func InitService(datastore Datastore, roDatastore ReadOnlyDatastore, repClient reputation.Client, geminiClient gemini.Client, geoCountryValidator GeoValidator, retry backoff.RetryFunc, metric metricSvc, gemini geminiSvc) (*Service, error) { - service := &Service{ - crMu: new(sync.RWMutex), - Datastore: datastore, - RoDatastore: roDatastore, - repClient: repClient, - geminiClient: geminiClient, - geoValidator: geoCountryValidator, - retry: retry, - metric: metric, - gemini: gemini, - } - return service, nil -} - -// Jobs - Implement srv.JobService interface -func (service *Service) Jobs() []srv.Job { - return service.jobs -} - -// ReadableDatastore returns a read only datastore if available, otherwise a normal datastore -func (service *Service) ReadableDatastore() ReadOnlyDatastore { - if service.RoDatastore != nil { - return service.RoDatastore - } - return service.Datastore -} - -// SetupService - create a new wallet service -func SetupService(ctx context.Context) (context.Context, *Service) { - logger := logging.Logger(ctx, "wallet.SetupService") - - db, err := NewWritablePostgres(viper.GetString("datastore"), false, "wallet_db") - if err != nil { - logger.Panic().Err(err).Msg("unable connect to wallet db") - } - - roDB, err := NewReadOnlyPostgres(viper.GetString("ro-datastore"), false, "wallet_ro_db") - if err != nil { - logger.Panic().Err(err).Msg("unable connect to wallet db") - } - - ctx = context.WithValue(ctx, appctx.RODatastoreCTXKey, roDB) - ctx = context.WithValue(ctx, appctx.DatastoreCTXKey, db) - - // add our command line params to context - ctx = context.WithValue(ctx, appctx.EnvironmentCTXKey, viper.Get("environment")) - - // jwt key is hex encoded string - decodedBitFlyerJWTKey, err := hex.DecodeString(viper.GetString("bitflyer-jwt-key")) - if err != nil { - logger.Error().Err(err).Msg("invalid bitflyer jwt key") - } - ctx = context.WithValue(ctx, appctx.BitFlyerJWTKeyCTXKey, decodedBitFlyerJWTKey) - - // setup reputation client - repClient, err := reputation.New() - // it's okay to not fatally fail if this environment is local and we cant make a rep client - if err != nil && os.Getenv("ENV") != "local" { - logger.Panic().Err(err).Msg("failed to initialize wallet service") - } - - ctx = context.WithValue(ctx, appctx.ReputationClientCTXKey, repClient) - - var geminiClient gemini.Client - if os.Getenv("GEMINI_ENABLED") == "true" { - geminiClient, err = gemini.New() - if err != nil { - logger.Panic().Err(err).Msg("failed to create gemini client") - } - ctx = context.WithValue(ctx, appctx.GeminiClientCTXKey, geminiClient) - } - - cfg, err := appaws.BaseAWSConfig(ctx, logger) - if err != nil { - logger.Panic().Err(err).Msg("failed to initialize wallet service") - } - - awsClient, err := appaws.NewClient(cfg) - if err != nil { - logger.Panic().Err(err).Msg("failed to initialize wallet service") - } - - // put the configured aws client on ctx - ctx = context.WithValue(ctx, appctx.AWSClientCTXKey, awsClient) - - // get the s3 bucket and object - bucket, bucketOK := ctx.Value(appctx.ParametersMergeBucketCTXKey).(string) - if !bucketOK { - logger.Panic().Err(errors.New("bucket not in context")). - Msg("failed to initialize wallet service") - } - object, ok := ctx.Value(appctx.DisabledWalletGeoCountriesCTXKey).(string) - if !ok { - logger.Panic().Err(errors.New("wallet geo countries disabled ctx key value not found")). - Msg("failed to initialize wallet service") - } - - config := Config{ - bucket: bucket, - object: object, - } - - geoCountryValidator := NewGeoCountryValidator(awsClient, config) - - mtc := metric.New() - gemx := newGeminix("passport", "drivers_license", "national_identity_card", "passport_card") - - s, err := InitService(db, roDB, repClient, geminiClient, geoCountryValidator, backoff.Retry, mtc, gemx) - if err != nil { - logger.Panic().Err(err).Msg("failed to initialize wallet service") - } - - _, err = s.RefreshCustodianRegionsWorker(ctx) - if err != nil { - logger.Error().Err(err).Msg("failed to initialize custodian regions") - } - - s.jobs = []srv.Job{ - { - Func: s.RefreshCustodianRegionsWorker, - Cadence: 15 * time.Minute, - Workers: 1, - }, - } - - if VerifiedWalletEnable { - s.jobs = append(s.jobs, srv.Job{ - Func: s.RunVerifiedWalletWorker, - Cadence: 1 * time.Second, - Workers: 1, - }) - } - - err = cmd.SetupJobWorkers(ctx, s.Jobs()) - if err != nil { - logger.Error().Err(err).Msg("error initializing job workers") - } - - return ctx, s -} - -func (service *Service) setCustodianRegions(custodianRegions custodian.Regions) { - service.crMu.Lock() - defer service.crMu.Unlock() - service.custodianRegions = custodianRegions -} - -func (service *Service) getCustodianRegions() custodian.Regions { - service.crMu.RLock() - defer service.crMu.RUnlock() - return service.custodianRegions -} - -// RegisterRoutes - register the wallet api routes given a chi.Mux -func RegisterRoutes(ctx context.Context, s *Service, r *chi.Mux) *chi.Mux { - // setup our wallet routes - r.Route("/v3/wallet", func(r chi.Router) { - // rate limited to 2 per minute... - // create wallet routes for our wallet providers - r.Post("/uphold", middleware.RateLimiter(ctx, 2)(middleware.InstrumentHandlerFunc( - "CreateUpholdWallet", CreateUpholdWalletV3)).ServeHTTP) - r.Post("/brave", middleware.RateLimiter(ctx, 2)(middleware.InstrumentHandlerFunc( - "CreateBraveWallet", CreateBraveWalletV3)).ServeHTTP) - - // if wallets are being migrated we do not want to over claim, we might go over the limit - if viper.GetBool("enable-link-drain-flag") { - // create wallet claim routes for our wallet providers - r.Post("/uphold/{paymentID}/claim", middleware.InstrumentHandlerFunc( - "LinkUpholdDepositAccount", LinkUpholdDepositAccountV3(s))) - r.Post("/bitflyer/{paymentID}/claim", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "LinkBitFlyerDepositAccount", LinkBitFlyerDepositAccountV3(s))).ServeHTTP) - r.Post("/gemini/{paymentID}/claim", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "LinkGeminiDepositAccount", LinkGeminiDepositAccountV3(s))).ServeHTTP) - r.Post("/zebpay/{paymentID}/claim", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "LinkZebPayDepositAccount", LinkZebPayDepositAccountV3(s))).ServeHTTP) - - // create wallet connect routes for our wallet providers - r.Post("/uphold/{paymentID}/connect", middleware.InstrumentHandlerFunc( - "LinkUpholdDepositAccount", LinkUpholdDepositAccountV3(s))) - r.Post("/bitflyer/{paymentID}/connect", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "LinkBitFlyerDepositAccount", LinkBitFlyerDepositAccountV3(s))).ServeHTTP) - r.Post("/gemini/{paymentID}/connect", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "LinkGeminiDepositAccount", LinkGeminiDepositAccountV3(s))).ServeHTTP) - r.Post("/zebpay/{paymentID}/connect", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "LinkZebPayDepositAccount", LinkZebPayDepositAccountV3(s))).ServeHTTP) - } - - r.Get("/linking-info", middleware.SimpleTokenAuthorizedOnly( - middleware.InstrumentHandlerFunc("GetLinkingInfo", GetLinkingInfoV3(s))).ServeHTTP) - - // get wallet routes - r.Get("/{paymentID}", middleware.InstrumentHandlerFunc( - "GetWallet", GetWalletV3)) - r.Get("/recover/{publicKey}", middleware.InstrumentHandlerFunc( - "RecoverWallet", RecoverWalletV3)) - - // get wallet balance routes - r.Get("/uphold/{paymentID}", middleware.InstrumentHandlerFunc( - "GetUpholdWalletBalance", GetUpholdWalletBalanceV3)) - }) - - r.Route("/v4/wallets", func(r chi.Router) { - r.Use(middleware.RateLimiter(ctx, 2)) - r.Post("/", middleware.InstrumentHandlerFunc("CreateWalletV4", CreateWalletV4(s))) - r.Patch("/{paymentID}", middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "UpdateWalletV4", UpdateWalletV4(s))).ServeHTTP) - r.Get("/{paymentID}", - middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "GetWalletV4", GetWalletV4)).ServeHTTP) - // get wallet balance routes - r.Get("/uphold/{paymentID}", - middleware.HTTPSignedOnly(s)(middleware.InstrumentHandlerFunc( - "GetUpholdWalletBalanceV4", GetUpholdWalletBalanceV4)).ServeHTTP) - }) - - return r -} - -// SubmitAnonCardTransaction validates and submits a transaction on behalf of an anonymous card -func (service *Service) SubmitAnonCardTransaction( - ctx context.Context, - walletID uuid.UUID, - transaction string, - destination string, -) (*walletutils.TransactionInfo, error) { - info, err := service.Datastore.GetWallet(ctx, walletID) - if err != nil { - return nil, errorutils.Wrap(err, "error getting wallet") - } - return service.SubmitCommitableAnonCardTransaction(ctx, info, transaction, destination, true) -} - -// GetWallet - get a wallet by id -func (service *Service) GetWallet(ctx context.Context, ID uuid.UUID) (*walletutils.Info, error) { - return service.Datastore.GetWallet(ctx, ID) -} - -// SubmitCommitableAnonCardTransaction submits a transaction -func (service *Service) SubmitCommitableAnonCardTransaction( - ctx context.Context, - info *walletutils.Info, - transaction string, - destination string, - confirm bool, -) (*walletutils.TransactionInfo, error) { - providerWallet, err := provider.GetWallet(ctx, *info) - if err != nil { - return nil, err - } - anonCard, ok := providerWallet.(*uphold.Wallet) - if !ok { - return nil, errors.New("only uphold wallets are supported") - } - - // FIXME needs to require the idempotency key - _, err = anonCard.VerifyAnonCardTransaction(ctx, transaction, destination) - if err != nil { - return nil, err - } - - // Submit and confirm since we are requiring the idempotency key - return anonCard.SubmitTransaction(ctx, transaction, confirm) -} - -// GetLinkingInfo - Get data about the linking info -func (service *Service) GetLinkingInfo(ctx context.Context, providerLinkingID, custodianID string) (map[string]LinkingInfo, error) { - // compute the provider linking id based on custodian id if there is one - - if custodianID != "" { - // generate a provider linking id - providerLinkingID = uuid.NewV5(ClaimNamespace, custodianID).String() - } - - infos, err := service.Datastore.GetLinkingLimitInfo(ctx, providerLinkingID) - if err != nil { - return infos, fmt.Errorf("unable to increase linking limit: %w", err) - } - return infos, nil -} - -// LinkBitFlyerWallet links a wallet and transfers funds to newly linked wallet -func (service *Service) LinkBitFlyerWallet(ctx context.Context, walletID uuid.UUID, depositID, accountHash string) (string, error) { - const ( - depositProvider = "bitflyer" - country = "JP" - ) - - err := validateCustodianLinking(ctx, service.Datastore, walletID, depositProvider) - if err != nil { - if errors.Is(err, errCustodianLinkMismatch) { - return "", errCustodianLinkMismatch - } - return "", handlers.WrapError(err, "failed to check linking mismatch", http.StatusInternalServerError) - } - - // In the controller validation, we verified that the account hash and deposit id were signed by bitflyer - // we also validated that this "info" signed the request to perform the linking with http signature - // we assume that since we got linkingInfo signed from BF that they are KYC - providerLinkingID := uuid.NewV5(ClaimNamespace, accountHash) - err = service.Datastore.LinkWallet(ctx, walletID.String(), depositID, providerLinkingID, depositProvider, country) - if err != nil { - if errors.Is(err, ErrUnusualActivity) { - return "", handlers.WrapError(err, "unable to link - unusual activity", http.StatusBadRequest) - } - - if errors.Is(err, ErrGeoResetDifferent) { - return "", handlers.WrapError(err, "mismatched provider account regions", http.StatusBadRequest) - } - - status := http.StatusInternalServerError - if errors.Is(err, ErrTooManyCardsLinked) { - status = http.StatusConflict - } - - return "", handlers.WrapError(err, "unable to link bitflyer wallets", status) - } - - return country, nil -} - -// LinkZebPayWallet links a wallet and transfers funds to newly linked wallet. -func (service *Service) LinkZebPayWallet(ctx context.Context, walletID uuid.UUID, verificationToken string) (string, error) { - const depositProvider = "zebpay" - - claims, err := parseZebPayClaims(ctx, verificationToken) - if err != nil { - return "", err - } - - if err := claims.validate(time.Now()); err != nil { - service.metric.LinkFailureZP(claims.CountryCode) - return "", err - } - - err = validateCustodianLinking(ctx, service.Datastore, walletID, depositProvider) - if err != nil { - service.metric.LinkFailureZP(claims.CountryCode) - - if errors.Is(err, errCustodianLinkMismatch) { - return "", errCustodianLinkMismatch - } - return "", handlers.WrapError(err, "failed to check linking mismatch", http.StatusInternalServerError) - } - - providerLinkingID := uuid.NewV5(ClaimNamespace, claims.AccountID) - if err := service.Datastore.LinkWallet(ctx, walletID.String(), claims.DepositID, providerLinkingID, depositProvider, claims.CountryCode); err != nil { - service.metric.LinkFailureZP(claims.CountryCode) - - if errors.Is(err, ErrUnusualActivity) { - return "", handlers.WrapError(err, "unable to link - unusual activity", http.StatusBadRequest) - } - - if errors.Is(err, ErrGeoResetDifferent) { - return "", handlers.WrapError(err, "mismatched provider account regions", http.StatusBadRequest) - } - - if errors.Is(err, ErrTooManyCardsLinked) { - return "", handlers.WrapError(err, "unable to link zebpay wallets", http.StatusConflict) - } - - return "", handlers.WrapError(err, "unable to link zebpay wallets", http.StatusInternalServerError) - } - - service.metric.LinkSuccessZP(claims.CountryCode) - - return claims.CountryCode, nil -} - -const errNoAcceptedDocumentType model.Error = "no accepted document type" - -// LinkGeminiWallet links a wallet to a Gemini account. -func (service *Service) LinkGeminiWallet(ctx context.Context, walletID uuid.UUID, verificationToken, depositID string) (string, error) { - cl, err := service.Datastore.GetCustodianLinkByWalletID(ctx, walletID) - if err != nil && !errors.Is(err, model.ErrNoWalletCustodian) { - return "", handlers.WrapError(err, "failed to check linking mismatch", http.StatusInternalServerError) - } - - const depositProvider = "gemini" - - if cl.isLinked() && !strings.EqualFold(cl.Custodian, depositProvider) { - return "", errCustodianLinkMismatch - } - - gc, ok := ctx.Value(appctx.GeminiClientCTXKey).(gemini.Client) - if !ok { - return "", handlers.WrapError(appctx.ErrNotInContext, "gemini client misconfigured", http.StatusInternalServerError) - } - - acc, err := gc.FetchValidatedAccount(ctx, verificationToken, depositID) - if err != nil { - return "", fmt.Errorf("failed to validate account: %w", err) - } - - service.metric.CountDocTypeByIssuingCntry(acc.ValidDocuments) - - linkingID := uuid.NewV5(ClaimNamespace, acc.ID) - // Some Gemini accounts do not have valid documents setup. For accounts that are already linked i.e. are - // re-authenticating we can fall back to the legacy country code. New or re-linkings should not fall back. - isAuth := cl.isLinked() && *cl.LinkingID == linkingID - issuingCountry := service.gemini.GetIssuingCountry(acc, isAuth) - if issuingCountry == "" { - return "", fmt.Errorf("failed to validate account: %w", errNoAcceptedDocumentType) - } - - if err := service.gemini.IsRegionAvailable(ctx, issuingCountry, service.custodianRegions); err != nil { - if errors.Is(err, errorutils.ErrInvalidCountry) { - // If a wallet has previously been linked i.e. has a prior linking, but the country is now invalid/blocked - // then we can allow the account to link due to its prior successful linking i.e. it is grandfathered. - // If there is no prior linking and the country is invalid/blocked then we should apply the current rules and block it. - hasPriorLinking, priorLinkingErr := service.Datastore.HasPriorLinking(ctx, walletID, linkingID) - if priorLinkingErr != nil && !errors.Is(err, sql.ErrNoRows) { - return "", fmt.Errorf("failed to check prior linkings: %w", priorLinkingErr) - } - - if !hasPriorLinking { - service.metric.LinkFailureGemini(issuingCountry) - return "", fmt.Errorf("failed to validate account: %w", err) - } - - } else { - // not err invalid country error - return "", fmt.Errorf("failed to validate account: %w", err) - } - } - service.metric.LinkSuccessGemini(issuingCountry) - - if err := service.Datastore.LinkWallet(ctx, walletID.String(), depositID, linkingID, depositProvider, issuingCountry); err != nil { - if errors.Is(err, ErrUnusualActivity) { - return "", handlers.WrapError(err, "unable to link - unusual activity", http.StatusBadRequest) - } - - if errors.Is(err, ErrGeoResetDifferent) { - return "", handlers.WrapError(err, "mismatched provider account regions", http.StatusBadRequest) - } - - if errors.Is(err, ErrTooManyCardsLinked) { - return "", handlers.WrapError(err, "unable to link gemini wallets", http.StatusConflict) - } - - return "", handlers.WrapError(err, "unable to link gemini wallets", http.StatusInternalServerError) - } - - return issuingCountry, nil -} - -// LinkUpholdWallet links an uphold.Wallet and transfers funds. -func (service *Service) LinkUpholdWallet(ctx context.Context, wallet uphold.Wallet, transaction string, _ *uuid.UUID) (string, error) { - const depositProvider = "uphold" - // do not confirm this transaction yet - info := wallet.GetWalletInfo() - - var ( - userID string - country string - probi decimal.Decimal - ) - - transactionInfo, err := wallet.VerifyTransaction(ctx, transaction) - if err != nil { - return "", handlers.WrapError( - errors.New("failed to verify transaction"), "transaction verification failure", - http.StatusForbidden) - } - - // add custodian regions to ctx going to client - _, ok := ctx.Value(appctx.CustodianRegionsCTXKey).(custodian.Regions) - if !ok { - cr := service.getCustodianRegions() - ctx = context.WithValue(ctx, appctx.CustodianRegionsCTXKey, &cr) - } - - walletID, err := uuid.FromString(info.ID) - if err != nil { - return "", fmt.Errorf("failed to parse uphold id: %w", err) - } - - err = validateCustodianLinking(ctx, service.Datastore, walletID, depositProvider) - if err != nil { - if errors.Is(err, errCustodianLinkMismatch) { - return "", errCustodianLinkMismatch - } - return "", handlers.WrapError(err, "failed to check linking mismatch", http.StatusInternalServerError) - } - - // verify that the user is kyc from uphold. (for all wallet provider cases) - if uID, ok, c, err := wallet.IsUserKYC(ctx, transactionInfo.Destination); err != nil { - // check if this gemini accountID has already been linked to this wallet, - if errors.Is(err, errorutils.ErrInvalidCountry) { - ok, priorLinkingErr := service.Datastore.HasPriorLinking( - ctx, walletID, uuid.NewV5(ClaimNamespace, userID)) - if priorLinkingErr != nil && !errors.Is(err, sql.ErrNoRows) { - return "", fmt.Errorf("failed to check prior linkings: %w", priorLinkingErr) - } - // if a wallet has a prior linking to this account, allow the invalid country, otherwise - // return the kyc error - if !ok { - // then pass back the original geo error - return "", err - } - // allow invalid country if there was a prior linking - } else { - return "", fmt.Errorf("wallet could not be kyc checked: %w", err) - } - } else if !ok { - // fail - return "", handlers.WrapError( - errors.New("user kyc did not pass"), - "KYC required", - http.StatusForbidden) - } else { - userID = uID - country = c - } - - // check kyc user id validity - if userID == "" { - return "", handlers.WrapError( - errors.New("user id not provided"), - "KYC required", - http.StatusForbidden) - } - - probi = transactionInfo.Probi - - providerLinkingID := uuid.NewV5(ClaimNamespace, userID) - // tx.Destination will be stored as UserDepositDestination in the wallet info upon linking - err = service.Datastore.LinkWallet(ctx, info.ID, transactionInfo.Destination, providerLinkingID, depositProvider, country) - if err != nil { - if errors.Is(err, ErrUnusualActivity) { - return "", handlers.WrapError(err, "unable to link - unusual activity", http.StatusBadRequest) - } - - if errors.Is(err, ErrGeoResetDifferent) { - return "", handlers.WrapError(err, "mismatched provider account regions", http.StatusBadRequest) - } - - status := http.StatusInternalServerError - if errors.Is(err, ErrTooManyCardsLinked) { - status = http.StatusConflict - } - - return "", handlers.WrapError(err, "unable to link uphold wallets", status) - } - - // if this wallet is linking a deposit account do not submit a transaction - if decimal.NewFromFloat(0).LessThan(probi) { - _, err := service.SubmitCommitableAnonCardTransaction(ctx, &info, transaction, "", true) - if err != nil { - return "", handlers.WrapError(err, "unable to transfer tokens", http.StatusBadRequest) - } - } - - return country, nil -} - -// DisconnectCustodianLink - removes the link to the custodian wallet that is active -func (service *Service) DisconnectCustodianLink(ctx context.Context, custodian string, walletID uuid.UUID) error { - if err := service.Datastore.DisconnectCustodialWallet(ctx, walletID); err != nil { - return handlers.WrapError(err, "unable to disconnect custodian wallet", http.StatusInternalServerError) - } - return nil -} - -// CreateRewardsWallet creates a brave rewards wallet and informs the reputation service. -// If either the local transaction or call to the reputation service fails then the wallet is not created. -func (service *Service) CreateRewardsWallet(ctx context.Context, publicKey string, geoCountry string) (*walletutils.Info, error) { - log := logging.Logger(ctx, "wallets.CreateRewardsWallet") - - valid, err := service.geoValidator.Validate(ctx, geoCountry) - if err != nil { - return nil, fmt.Errorf("error validating geo country: %w", err) - } - - if !valid { - return nil, errGeoCountryDisabled - } - - var altCurrency = altcurrency.BAT - var info = &walletutils.Info{ - ID: uuid.NewV5(ClaimNamespace, publicKey).String(), - Provider: "brave", - PublicKey: publicKey, - AltCurrency: &altCurrency, - } - - ctx, tx, rollback, commit, err := getTx(ctx, service.Datastore) - if err != nil { - return nil, fmt.Errorf("error creating transaction: %w", err) - } - defer rollback() - - err = service.Datastore.InsertWalletTx(ctx, tx, info) - if err != nil { - var pgErr *pq.Error - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { // unique constraint violation - if info != nil { - log.Error().Err(err).Interface("info", info). - Msg("error InsertWalletTx") - } - return nil, errRewardsWalletAlreadyExists - } - } - return nil, fmt.Errorf("error inserting rewards wallet: %w", err) - } - - upsertReputationSummary := func() (interface{}, error) { - return nil, service.repClient.UpsertReputationSummary(ctx, info.ID, geoCountry) - } - - _, err = service.retry(ctx, upsertReputationSummary, retryPolicy, canRetry(nonRetriableErrors)) - if err != nil { - return nil, fmt.Errorf("error calling reputation service: %w", err) - } - - err = commit() - if err != nil { - return nil, fmt.Errorf("error comitting rewards wallet transaction: %w", err) - } - - return info, nil -} - -// RefreshCustodianRegionsWorker - get the custodian regions from the merge param bucket -func (service *Service) RefreshCustodianRegionsWorker(ctx context.Context) (bool, error) { - useCustodianRegions, featureOK := ctx.Value(appctx.UseCustodianRegionsCTXKey).(bool) - if featureOK && !useCustodianRegions { - // do not attempt no error - return false, nil - } - // get aws client - client, clientOK := ctx.Value(appctx.AWSClientCTXKey).(*appaws.Client) - if !clientOK { - return true, errors.New("cannot run refresh custodian regions, no client") - } - // get the bucket and if we are feature flagged on - bucket, bucketOK := ctx.Value(appctx.ParametersMergeBucketCTXKey).(string) - if !bucketOK { - return true, errors.New("cannot run refresh custodian regions, no bucket") - } - - select { - case <-ctx.Done(): - return true, ctx.Err() - default: - // use client to put the custodian regions on ctx - custodianRegions, err := custodian.ExtractCustodianRegions(ctx, client, bucket) - if err != nil { - return true, fmt.Errorf("error running refresh custodian regions: %w", err) - } - // write custodian regions to service - service.setCustodianRegions(*custodianRegions) - return true, nil - } -} - -func (service *Service) RunVerifiedWalletWorker(ctx context.Context) (bool, error) { - for { - select { - case <-ctx.Done(): - return true, ctx.Err() - default: - _, err := service.Datastore.SendVerifiedWalletOutbox(ctx, service.repClient, service.retry) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return true, nil - } - return true, fmt.Errorf("error running send verified wallet request: %w", err) - } - } - } -} - -func canRetry(nonRetriableErrors []int) func(error) bool { - return func(err error) bool { - var eb *errorutils.ErrorBundle - switch { - case errors.As(err, &eb): - if hs, ok := eb.Data().(clients.HTTPState); ok { - for _, httpStatusCode := range nonRetriableErrors { - if hs.Status == httpStatusCode { - return false - } - } - return true - } - } - return false - } -} - -type claimsZP struct { - Iat int64 `json:"iat"` - Exp int64 `json:"exp"` - DepositID string `json:"depositId"` - AccountID string `json:"accountId"` - Valid bool `json:"isValid"` - CountryCode string `json:"countryCode"` -} - -func (c *claimsZP) validate(now time.Time) error { - if c.Iat <= 0 { - return errZPInvalidIat - } - - if c.Exp <= 0 { - return errZPInvalidExp - } - - if !c.isKYC() { - return errZPInvalidKYC - } - - // Make sure deposit id exists - if c.DepositID == "" { - return errZPInvalidDepositID - } - - // Get the account id. - if c.AccountID == "" { - return errZPInvalidAccountID - } - - if !strings.EqualFold(c.CountryCode, "IN") { - return errorutils.ErrInvalidCountry - } - - return c.validateTime(now) -} - -func (c *claimsZP) isKYC() bool { - return c.Valid -} - -func (c *claimsZP) validateTime(now time.Time) error { - if now.Before(time.Unix(c.Iat, 0)) { - return errZPInvalidAfter - } - - if now.After(time.Unix(c.Exp, 0)) { - return errZPInvalidBefore - } - - return nil -} - -func validateCustodianLinking(ctx context.Context, storage Datastore, walletID uuid.UUID, depositProvider string) error { - c, err := storage.GetCustodianLinkByWalletID(ctx, walletID) - if err != nil && !errors.Is(err, model.ErrNoWalletCustodian) { - return err - } - - // if there are no instances of wallet custodian then it is - // considered a new linking and therefore valid. - if c == nil { - return nil - } - - if !strings.EqualFold(c.Custodian, depositProvider) { - return errCustodianLinkMismatch - } - - return nil -} - -const ( - errZPParseToken model.Error = "zebpay linking info parsing failed" - errZPNoHeaders model.Error = "linking info token invalid no headers" - errZPInvalidToken model.Error = "linking info token invalid" - errZPValidationFailed model.Error = "zebpay linking info validation failed" -) - -func parseZebPayClaims(ctx context.Context, verificationToken string) (claimsZP, error) { - const msgBadConf = "zebpay linking validation misconfigured" - linkingKeyB64, ok := ctx.Value(appctx.ZebPayLinkingKeyCTXKey).(string) - if !ok { - return claimsZP{}, handlers.WrapError(appctx.ErrNotInContext, msgBadConf, http.StatusInternalServerError) - } - - decodedJWTKey, err := base64.StdEncoding.DecodeString(linkingKeyB64) - if err != nil { - return claimsZP{}, handlers.WrapError(appctx.ErrNotInContext, msgBadConf, http.StatusInternalServerError) - } - - tok, err := jwt.ParseSigned(verificationToken) - if err != nil { - return claimsZP{}, handlers.WrapError(errZPParseToken, errZPParseToken.Error(), http.StatusBadRequest) - } - - if len(tok.Headers) == 0 { - return claimsZP{}, handlers.WrapError(errZPNoHeaders, errZPNoHeaders.Error(), http.StatusBadRequest) - } - - for i := range tok.Headers { - if tok.Headers[i].Algorithm != "HS256" { - return claimsZP{}, handlers.WrapError(errZPInvalidToken, errZPInvalidToken.Error(), http.StatusBadRequest) - } - } - - var claims claimsZP - if err := tok.Claims(decodedJWTKey, &claims); err != nil { - return claimsZP{}, handlers.WrapError(errZPValidationFailed, errZPValidationFailed.Error(), http.StatusBadRequest) - } - - return claims, nil -} diff --git a/services/wallet/service_test.go b/services/wallet/service_test.go deleted file mode 100644 index 7e6b3ef31..000000000 --- a/services/wallet/service_test.go +++ /dev/null @@ -1,311 +0,0 @@ -package wallet - -import ( - "context" - "encoding/base64" - "net/http" - "testing" - "time" - - appctx "github.com/brave-intl/bat-go/libs/context" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/handlers" - should "github.com/stretchr/testify/assert" - must "github.com/stretchr/testify/require" - "gopkg.in/square/go-jose.v2" - "gopkg.in/square/go-jose.v2/jwt" -) - -func TestClaimsZP(t *testing.T) { - type tcGiven struct { - now time.Time - claims claimsZP - } - - type testCase struct { - name string - given tcGiven - exp error - } - - tests := []testCase{ - { - name: "invalid_iat", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidIat, - }, - - { - name: "invalid_exp", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidExp, - }, - - { - name: "invalid_kyc", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidKYC, - }, - - { - name: "invalid_deposit", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - AccountID: "account_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidDepositID, - }, - - { - name: "invalid_account", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidAccountID, - }, - - { - name: "invalid_before_iat", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidAfter, - }, - - { - name: "invalid_after_exp", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 3, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "IN", - }, - }, - exp: errZPInvalidBefore, - }, - { - name: "invalid_country_code", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 3, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "US", - }, - }, - exp: errorutils.ErrInvalidCountry, - }, - { - name: "valid", - given: tcGiven{ - now: time.Date(2023, time.August, 16, 1, 1, 1, 0, time.UTC), - claims: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - Valid: true, - DepositID: "deposit_id", - AccountID: "account_id", - CountryCode: "IN", - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - act := tc.given.claims.validate(tc.given.now) - should.Equal(t, tc.exp, act) - }) - } -} - -func Test_parseZebPayClaims(t *testing.T) { - type tcGiven struct { - ctxKey appctx.CTXKey - secret string - sigAlgo string - zpLinkingKey string - claims map[string]interface{} - } - - type tcExpected struct { - claimsZP claimsZP - appErr error - } - - tests := []struct { - name string - given tcGiven - expected tcExpected - }{ - { - name: "success", - given: tcGiven{ - ctxKey: appctx.ZebPayLinkingKeyCTXKey, - secret: "test secret", - zpLinkingKey: base64.StdEncoding.EncodeToString([]byte("test secret")), - claims: map[string]interface{}{ - "iat": time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - "exp": time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - "depositId": "deposit_id", - "accountId": "account_id", - "isValid": true, - "countryCode": "", - }, - sigAlgo: "HS256", - }, - expected: tcExpected{ - claimsZP: claimsZP{ - Iat: time.Date(2023, time.August, 16, 1, 1, 0, 0, time.UTC).Unix(), - Exp: time.Date(2023, time.August, 16, 1, 1, 2, 0, time.UTC).Unix(), - DepositID: "deposit_id", - AccountID: "account_id", - Valid: true, - }, - }, - }, - { - name: "bad_config_key", - given: tcGiven{ - ctxKey: "bad_key", - sigAlgo: "HS256", - }, - expected: tcExpected{ - appErr: &handlers.AppError{ - Cause: appctx.ErrNotInContext, - Message: "zebpay linking validation misconfigured", - Code: http.StatusInternalServerError, - }, - }, - }, - { - name: "bad_config_decode", - given: tcGiven{ - ctxKey: appctx.ZebPayLinkingKeyCTXKey, - secret: "test secret", - sigAlgo: "HS256", - zpLinkingKey: "!!invalid_64!!", - }, - expected: tcExpected{ - appErr: &handlers.AppError{ - Cause: appctx.ErrNotInContext, - Message: "zebpay linking validation misconfigured", - Code: http.StatusInternalServerError, - }, - }, - }, - { - name: "wrong_signature_algorithm", - given: tcGiven{ - ctxKey: appctx.ZebPayLinkingKeyCTXKey, - secret: "test secret", - sigAlgo: "HS384", - zpLinkingKey: base64.StdEncoding.EncodeToString([]byte("test secret")), - }, - expected: tcExpected{ - appErr: &handlers.AppError{ - Cause: errZPInvalidToken, - Message: errZPInvalidToken.Error(), - Code: http.StatusBadRequest, - }, - }, - }, - { - name: "error_deserializing_claims", - given: tcGiven{ - ctxKey: appctx.ZebPayLinkingKeyCTXKey, - secret: "test secret", - sigAlgo: "HS256", - zpLinkingKey: base64.StdEncoding.EncodeToString([]byte("test secret")), - claims: map[string]interface{}{ - "accountId": 1, // invalid account type - }, - }, - expected: tcExpected{ - appErr: &handlers.AppError{ - Cause: errZPValidationFailed, - Message: errZPValidationFailed.Error(), - Code: http.StatusBadRequest, - }, - }, - }, - } - - for i := range tests { - tc := tests[i] - - t.Run(tc.name, func(t *testing.T) { - signer, err := jose.NewSigner(jose.SigningKey{ - Algorithm: jose.SignatureAlgorithm(tc.given.sigAlgo), - Key: []byte(tc.given.secret), - }, (&jose.SignerOptions{}).WithType("JWT")) - must.Equal(t, nil, err) - - verificationToken, err := jwt.Signed(signer).Claims(tc.given.claims).CompactSerialize() - must.Equal(t, nil, err) - - ctx := context.WithValue(context.Background(), tc.given.ctxKey, tc.given.zpLinkingKey) - - actual, err := parseZebPayClaims(ctx, verificationToken) - should.Equal(t, tc.expected.claimsZP, actual) - should.Equal(t, tc.expected.appErr, err) - }) - } -} diff --git a/services/wallet/wallettest/wallettest.go b/services/wallet/wallettest/wallettest.go deleted file mode 100644 index c27e38eb3..000000000 --- a/services/wallet/wallettest/wallettest.go +++ /dev/null @@ -1,40 +0,0 @@ -// Package wallettest provides utilities for testing wallets. Do not import this into non-test code. -package wallettest - -import ( - "github.com/brave-intl/bat-go/libs/datastore" - "github.com/jmoiron/sqlx" - "github.com/stretchr/testify/assert" - "testing" -) - -var tables = []string{"claim_creds", "claims", "wallets", "issuers", "promotions"} - -// Migrate - perform a migration for skus -func Migrate(t *testing.T) { - postgres, err := datastore.NewPostgres("", false, "wallet_db") - assert.NoError(t, err) - - migrate, err := postgres.NewMigrate() - assert.NoError(t, err) - - version, dirty, _ := migrate.Version() - if dirty { - assert.NoError(t, migrate.Force(int(version))) - } - - if version > 0 { - assert.NoError(t, migrate.Down()) - } - - err = postgres.Migrate() - assert.NoError(t, err) -} - -// CleanDB - clean up the test db fixtures -func CleanDB(t *testing.T, datastore *sqlx.DB) { - for _, table := range tables { - _, err := datastore.Exec("delete from " + table) - assert.NoError(t, err) - } -} diff --git a/tools/Dockerfile b/tools/Dockerfile deleted file mode 100644 index e1003171a..000000000 --- a/tools/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM golang:1.19-alpine as builder - -# put certs in builder image -RUN apk update -RUN apk add -U --no-cache ca-certificates && update-ca-certificates -RUN apk add make -RUN apk add build-base -RUN apk add git -RUN apk add bash - -ARG VERSION -ARG BUILD_TIME -ARG COMMIT - -WORKDIR /src/ -COPY . ./ -RUN go mod download - -RUN CGO_ENABLED=0 GOOS=linux go build \ - -ldflags "-w -s -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME} -X main.commit=${COMMIT}" \ - -o bat-go main.go - -FROM alpine:3.18 as base -# put certs in artifact from builder -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=builder /src/bat-go /bin/ - -FROM base as payments -CMD ["bat-go", "serve", "nitro", "inside-enclave", "--log-address", "vm(3):2345", "--egress-address", "vm(3):1234", "--upstream-url", "http://0.0.0.0:8080", "--address", ":8080"] - -FROM base as artifact -COPY --from=builder /src/migrations/ /migrations/ -EXPOSE 3333 -CMD ["bat-go", "serve", "grant", "--enable-job-workers", "true"] - diff --git a/tools/Makefile b/tools/Makefile deleted file mode 100644 index a305db155..000000000 --- a/tools/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -GIT_VERSION := $(shell git describe --abbrev=8 --dirty --always --tags) -GIT_COMMIT := $(shell git rev-parse --short HEAD) -BUILD_TIME := $(shell date +%s) -VAULT_VERSION=0.10.1 -TEST_PKG?=./... -TEST_FLAGS= --tags=$(TEST_TAGS) $(TEST_PKG) -ifdef TEST_RUN - TEST_FLAGS = --tags=$(TEST_TAGS) $(TEST_PKG) --run=$(TEST_RUN) -endif - -.PHONY: all buildcmd docker test create-json-schema lint clean -all: test create-json-schema buildcmd - -.DEFAULT: buildcmd - -buildcmd: - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -v -ldflags "-w -s -X main.version=${GIT_VERSION} -X main.buildTime=${BUILD_TIME} -X main.commit=${GIT_COMMIT}" -o bat-go-tools main.go - -docker: - docker rmi -f bat-go-tools:latest - docker build --build-arg COMMIT=$(GIT_COMMIT) --build-arg VERSION=$(GIT_VERSION) \ - --build-arg BUILD_TIME=$(BUILD_TIME) -t bat-go-tools:$(GIT_VERSION)$(BUILD_TIME) . - docker tag bat-go:$(GIT_VERSION)$(BUILD_TIME) bat-go-tools:latest - -settlement-tools: - $(eval GOOS?=darwin) - $(eval GOARCH?=amd64) - rm -rf target/settlement-tools - mkdir -p target/settlement-tools - cp settlement/config.hcl target/settlement-tools/ - cp settlement/README.md target/settlement-tools/ - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -v -ldflags "-w -s -X main.version=${GIT_VERSION} -X main.buildTime=${BUILD_TIME} -X main.commit=${GIT_COMMIT}" -o target/settlement-tools/bat-cli - GOOS=$(GOOS) GOARCH=$(GOARCH) make download-vault - -docker-settlement-tools: - docker rmi -f brave/settlement-tools:latest - docker build -f settlement/Dockerfile --build-arg COMMIT=$(GIT_COMMIT) --build-arg VERSION=$(GIT_VERSION) \ - --build-arg BUILD_TIME=$(BUILD_TIME) -t brave/settlement-tools:$(GIT_VERSION)$(BUILD_TIME) . - docker tag brave/settlement-tools:$(GIT_VERSION)$(BUILD_TIME) brave/settlement-tools:latest - -grant-signing-tools: - $(eval GOOS?=darwin) - $(eval GOARCH?=amd64) - rm -rf target/grant-signing-tools - mkdir -p target/grant-signing-tools - GOOS=$(GOOS) GOARCH=$(GOARCH) make target/grant-signing-tools/create-tokens - GOOS=$(GOOS) GOARCH=$(GOARCH) make target/grant-signing-tools/create-tokens-ads - GOOS=$(GOOS) GOARCH=$(GOARCH) make target/grant-signing-tools/verify-tokens - -download-vault: - cd target/settlement-tools && curl -Os https://releases.hashicorp.com/vault/$(VAULT_VERSION)/vault_$(VAULT_VERSION)_$(GOOS)_$(GOARCH).zip - cd target/settlement-tools && curl -Os https://releases.hashicorp.com/vault/$(VAULT_VERSION)/vault_$(VAULT_VERSION)_SHA256SUMS - cd target/settlement-tools && curl -Os https://releases.hashicorp.com/vault/$(VAULT_VERSION)/vault_$(VAULT_VERSION)_SHA256SUMS.sig - cd target/settlement-tools && gpg --verify vault_$(VAULT_VERSION)_SHA256SUMS.sig vault_$(VAULT_VERSION)_SHA256SUMS - cd target/settlement-tools && grep $(GOOS)_$(GOARCH) vault_$(VAULT_VERSION)_SHA256SUMS | shasum -a 256 -c - cd target/settlement-tools && unzip -o vault_$(VAULT_VERSION)_$(GOOS)_$(GOARCH).zip vault && rm vault_$(VAULT_VERSION)_* - -vault: - ./target/settlement-tools/vault server -config=./target/settlement-tools/config.hcl - -vault-clean: - rm -rf share-0.gpg target/settlement-tools/vault-data vault-data - -test: - GODEBUG=x509ignoreCN=0 go test -count 1 -v -p 1 $(TEST_FLAGS) - -format: - gofmt -s -w ./ - -format-lint: - make format && make lint -lint: - golangci-lint run -E gofmt -E revive --exclude-use-default=false diff --git a/tools/cmd/README.md b/tools/cmd/README.md deleted file mode 100644 index 01b2cedaf..000000000 --- a/tools/cmd/README.md +++ /dev/null @@ -1,236 +0,0 @@ -Below is the command structure for bat-go microservices using cobra - -## create a macaroon -```bash -# needs an example.yaml file to work -./bat-go macaroon create -``` -with options -```bash -MACAROON_SECRET=a9ed2c16-3cf6-446f-8978-e707bead3979 \ -./bat-go macaroon create --config "macaroon-config.yaml" -``` - -## start rewards rest server -```bash -./bat-go serve rewards rest -``` -with options -```bash -./bat-go serve rewards rest \ - --config "config.yaml" \ - --ratios-token "abc" --ratios-service "123" --environment "local" \ - --base-currency "USD" --address ":4321" -``` - - -## check server fingerprints -```bash -./bat-go get-cert-fingerprint "brave.com:443" -``` - -## paypal settlement - -### transform -```bash -./bat-go settlement paypal transform \ - --input "paypal-settlement-from-antifraud.json" \ - --currency "JPY" -``` - -with other options -```bash -./bat-go settlement paypal transform \ - --input "paypal-settlement-from-antifraud.json" \ - --currency "JPY" \ - --rate "104.75" \ - --out "paypal-settlement-from-antifraud-complete.json" -``` - -### complete -```bash -./bat-go settlement paypal complete \ - --input "paypal-settlement-from-antifraud.json" \ - --txn-id "30ad1991-b2d3-4897-ae52-09efcd174235" -``` - -with output option -```bash -./bat-go settlement paypal complete \ - --input "paypal-settlement-from-antifraud.json" \ - --txn-id "30ad1991-b2d3-4897-ae52-09efcd174235" \ - --out "paypal-settlement-complete.json" -``` - -### email -```bash -./bat-go settlement paypal email \ - --input "paypal-settlement-from-antifraud.json" -``` - -## gemini settlement - -### upload - -```bash -./bat-go settlement gemini upload \ - --input "gemini-contribution-signed.json" \ - --all-txs-input "4ae996f5-679b-46c6-9aea-9892f763ffe6" \ - --sig 0 -``` - -with output -```bash -./bat-go settlement gemini upload \ - --input "gemini-contribution-signed.json" \ - --all-txs-input "4ae996f5-679b-46c6-9aea-9892f763ffe6" \ - --sig 0 \ - --out "gemini-contribution-signed-completed.json" -``` - -### checkstatus - -```bash -./bat-go settlement gemini checkstatus \ - --input "gemini-contribution-signed.json" \ - --all-txs-input "4ae996f5-679b-46c6-9aea-9892f763ffe6" -``` - -## bitflyer settlement - -equivalent envs are available as flags `ENV_KEY` -> `--env-key` - -### refresh token -After running the refres token command, you will need to copy the value in the printed `auth.access_token` field into your `.env` file and source that file. This can now be used with the other bitflyer commands. The env name should be `BITFLYER_TOKEN`. -```bash -BITFLYER_CLIENT_ID= -BITFLYER_CLIENT_SECRET= -BITFLYER_EXTRA_CLIENT_SECRET= -BITFLYER_SERVER= -./bat-go settlement bitflyer token -``` - -at this point, it makes sense to run the `sign-settlement` command so that transactions are split across multiple files, however this is not strictly necessary to do because we do all of the transforms needed in the upload step. - -### upload - -```bash -BITFLYER_SOURCE_FROM=tipping -BITFLYER_SERVER= -# omit to execute -BITFLYER_DRYRUN=1 # seconds to delay -./bat-go bitflyer upload \ - --in "bitflyer-transactions.json" \ - --exclude-limited true # if a transaction ever hits transfer limit, do not send it -``` - -### checkstatus - -```bash -BITFLYER_SOURCE_FROM=tipping -BITFLYER_SERVER= -./bat-go bitflyer checkstatus \ - --in "bitflyer-transactions.json" -``` - -## wallet - -### create - -```bash -./bat-go wallet create \ - --provider "uphold" \ - --name "test" -``` - -### vault create wallet -```bash -./bat-go vault create-wallet -``` -create offline -```bash -./bat-go vault create-wallet --offline true -``` - -### transfer funds - -with vault inputs -```bash -./bat-go wallet transfer-funds \ - --provider "uphold" \ - --from "1234567890" \ - --to "1234567890" \ - --usevault true \ # get secrets from vault - --value "10.5" -``` - -with env inputs -```bash -ED25519_PRIVATE_KEY= \ -UPHOLD_PROVIDER_ID= \ -./bat-go wallet transfer-funds \ - --provider "uphold" \ - --from "1234567890" \ - --to "1234567890" \ - --value "10.5" -``` - -## vault - -### init -```bash -./bat-go vault init \ - --key-shares 1 \ - --key-threshold 1 \ - ./key.asc -``` - -### import key - -```bash -ED25519_PRIVATE_KEY= -ED25519_PUBLIC_KEY= -UPHOLD_PROVIDER_ID= -GEMINI_CLIENT_ID= -GEMINI_CLIENT_KEY= -GEMINI_CLIENT_SECRET= -./bat-go vault import-key \ - --config "config.yaml" -``` - -only import a subset of the keys with `--wallet-refs` -```bash -./bat-go vault import-key \ - --config "config.yaml" \ - --wallet-refs "gemini-referral" -``` - -### sign settlement - -uses inputs from vault -```bash -./bat-go vault sign-settlement \ - --config "config.yaml" \ - --input "contributions.json" \ - --providers "uphold,gemini" -``` - -### unseal - -unseals vault -```bash -gpg -d ./share-0.gpg | ./bat-go vault unseal -``` - -## generate - -### json-schema - -generates json schemas -```bash -go run main.go generate json-schema -``` -with override -```bash -go run main.go generate json-schema --overwrite -``` diff --git a/tools/cmd/generate.go b/tools/cmd/generate.go deleted file mode 100644 index ac4f33ac7..000000000 --- a/tools/cmd/generate.go +++ /dev/null @@ -1,95 +0,0 @@ -package cmd - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "strings" - - "github.com/alecthomas/jsonschema" - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func init() { - cmdutils.RootCmd.AddCommand(GenerateCmd) - GenerateCmd.AddCommand(JSONSchemaCmd) - - // overwrite - defaults to false - JSONSchemaCmd.Flags().Bool("overwrite", false, - "overwrite the existing json schema files") - cmdutils.Must(viper.BindPFlag("overwrite", JSONSchemaCmd.Flags().Lookup("overwrite"))) -} - -// GenerateCmd is the generate command -var GenerateCmd = &cobra.Command{ - Use: "generate", - Short: "entrypoint to generate subcommands", -} - -// JSONSchemaCmd is the json schema command -var JSONSchemaCmd = &cobra.Command{ - Use: "json-schema", - Short: "entrypoint to generate json schema for project", - Run: cmdutils.Perform("generate json schema", jsonSchemaRun), -} - -// jsonSchemaRun - main entrypoint for the `generate json-schema` subcommand -func jsonSchemaRun(command *cobra.Command, args []string) error { - ctx := command.Context() - logger, err := appctx.GetLogger(ctx) - if err != nil { - return err - } - overwrite, err := command.Flags().GetBool("overwrite") - if err != nil { - return err - } - logger.Info().Msg("starting json-schema generation") - - // Wallet Outputs ./wallet/outputs.go - for _, t := range APIResponseTypes { - - logger.Info().Str("path", t.PkgPath()).Str("name", t.Name()).Str("str", t.String()).Msg("type being processed") - - schema, err := jsonschema.ReflectFromType(t).MarshalJSON() - if err != nil { - return fmt.Errorf("failed to generate json schema: %w", err) - } - - parts := strings.Split(t.String(), ".") - - // read old schema file - existingSchema, err := ioutil.ReadFile( - fmt.Sprintf("../schema/%s/%s", parts[0], parts[1])) - if err != nil { - logger.Info().Err(err).Msg("could not find existing schema file, might be a new api") - } else { - // test equality of schema file with what we just generated - if !bytes.Equal(existingSchema, schema) { - if overwrite { - logger.Warn().Msg(fmt.Sprintf("Schema has changed: %s.%s", parts[0], parts[1])) - } else { - return fmt.Errorf("schema has changed: %s.%s", parts[0], parts[1]) - } - } - } - - if overwrite { - err = ioutil.WriteFile( - fmt.Sprintf("../schema/%s/%s", parts[0], parts[1]), - schema, 0644) - if err != nil { - return fmt.Errorf("failed to generate json schema: %w", err) - } - } - - fmt.Fprintf(os.Stdout, "%s\n", schema) - } - - logger.Info().Msg("completed json-schema generation") - return nil -} diff --git a/tools/cmd/get-cert-fingerprint.go b/tools/cmd/get-cert-fingerprint.go deleted file mode 100644 index 33f060529..000000000 --- a/tools/cmd/get-cert-fingerprint.go +++ /dev/null @@ -1,63 +0,0 @@ -package cmd - -import ( - "context" - "crypto/tls" - "errors" - - rootcmd "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/pindialer" - "github.com/spf13/cobra" -) - -var ( - // GetCertFingerprintCmd is the GetCertFingerprint command - GetCertFingerprintCmd = &cobra.Command{ - Use: "get-cert-fingerprint", - Short: "A helper for fetching tls fingerprint info for pinning", - Run: rootcmd.Perform("get cert fingerprint", GetCertFingerprint), - } -) - -func init() { - rootcmd.RootCmd.AddCommand(GetCertFingerprintCmd) -} - -// GetCertFingerprint runs the command for GetCertFingerprint -func GetCertFingerprint(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("no arguments detected") - } - return CheckFingerprints(cmd.Context(), args) -} - -// CheckFingerprints checks the fingerprints at the following address -func CheckFingerprints(ctx context.Context, addresses []string) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - - for _, address := range addresses { - logger.Info(). - Str("address", address). - Msg("dialing") - c, err := tls.Dial("tcp", address, nil) - if err != nil { - return err - } - prints, err := pindialer.GetFingerprints(c) - if err != nil { - return err - } - for key, value := range prints { - logger.Info(). - Str("issuer", key). - Str("fingerprint", value). - Msg("issuer fingerprint") - } - } - return nil -} diff --git a/tools/cmd/schema.go b/tools/cmd/schema.go deleted file mode 100644 index 7492a2dea..000000000 --- a/tools/cmd/schema.go +++ /dev/null @@ -1,19 +0,0 @@ -package cmd - -import ( - "reflect" - - "github.com/brave-intl/bat-go/services/rewards" - "github.com/brave-intl/bat-go/services/wallet" -) - -var ( - // APIResponseTypes - A list of all API response types used in bat-go services - // primarily for auto generating the json-schema for each response type - APIResponseTypes = []reflect.Type{ - reflect.TypeOf(wallet.ResponseV3{}), - reflect.TypeOf(wallet.BalanceResponseV3{}), - reflect.TypeOf(wallet.LinkBraveDepositAccountRequest{}), - reflect.TypeOf(rewards.ParametersV1{}), - } -) diff --git a/tools/go.mod b/tools/go.mod deleted file mode 100644 index d467b8e67..000000000 --- a/tools/go.mod +++ /dev/null @@ -1,221 +0,0 @@ -module github.com/brave-intl/bat-go/tools - -go 1.18 - -require ( - github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b - github.com/brave-intl/bat-go v1.0.2 - github.com/brave-intl/bat-go/libs v1.0.2 - github.com/brave-intl/bat-go/services v1.0.2 - github.com/getsentry/sentry-go v0.14.0 - github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 - github.com/golang/mock v1.6.0 - github.com/google/uuid v1.3.0 - github.com/hashicorp/vault v1.12.7 - github.com/hashicorp/vault/api v1.8.1 - github.com/hashicorp/vault/sdk v0.6.1-0.20230427140652-b4b396ffc14f - github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 - github.com/rs/zerolog v1.28.0 - github.com/satori/go.uuid v1.2.0 - github.com/shengdoushi/base58 v1.0.0 - github.com/shopspring/decimal v1.3.1 - github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.13.0 - github.com/stretchr/testify v1.8.1 - golang.org/x/crypto v0.14.0 - golang.org/x/term v0.13.0 - gopkg.in/macaroon.v2 v2.1.0 - gopkg.in/yaml.v2 v2.4.0 - gotest.tools v2.2.0+incompatible -) - -replace github.com/brave-intl/bat-go/cmd => ../cmd - -replace github.com/brave-intl/bat-go/libs => ../libs - -replace github.com/brave-intl/bat-go/services => ../services - -require ( - cloud.google.com/go/compute v1.18.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.11.0 // indirect - cloud.google.com/go/kms v1.6.0 // indirect - cloud.google.com/go/monitoring v1.8.0 // indirect - github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.28 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/DataDog/datadog-go v4.8.3+incompatible // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/aliyun/alibaba-cloud-sdk-go v1.61.1831 // indirect - github.com/armon/go-metrics v0.4.1 // indirect - github.com/armon/go-radix v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go v1.44.206 // indirect - github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/config v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.12.23 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 // indirect - github.com/aws/smithy-go v1.13.5 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/btcsuite/btcutil v1.0.2 // indirect - github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect - github.com/circonus-labs/circonusllhist v0.1.5 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect - github.com/go-openapi/analysis v0.21.4 // indirect - github.com/go-openapi/errors v0.20.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/loads v0.21.2 // indirect - github.com/go-openapi/spec v0.20.7 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-openapi/validate v0.22.0 // indirect - github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 // indirect - github.com/golang-migrate/migrate/v4 v4.15.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/go-metrics-stackdriver v0.5.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.3.1 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-kms-wrapping/v2 v2.0.5 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.4 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.1 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.0 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.5 // indirect - github.com/hashicorp/go-retryablehttp v0.7.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hashicorp/hcp-sdk-go v0.23.0 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/huandu/xstrings v1.3.2 // indirect - github.com/iancoleman/orderedmap v0.2.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/lib/pq v1.10.7 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/mitchellh/cli v1.1.4 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/natefinch/atomic v1.0.1 // indirect - github.com/oklog/run v1.1.0 // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/oracle/oci-go-sdk/v60 v60.0.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/pierrec/lz4 v2.6.1+incompatible // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/posener/complete v1.2.3 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/sony/gobreaker v0.5.0 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect - github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/x448/float16 v0.8.4 // indirect - go.mongodb.org/mongo-driver v1.10.3 // indirect - go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.1.0 // indirect - golang.org/x/tools v0.14.0 // indirect - google.golang.org/api v0.110.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/tools/go.sum b/tools/go.sum deleted file mode 100644 index 6c6415ff5..000000000 --- a/tools/go.sum +++ /dev/null @@ -1,2396 +0,0 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.101.1/go.mod h1:55HwjsGW4CHD3JrNuMdZtSDsgTs0CuCB/bBTugD+7AA= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.11.0 h1:kwCWfKwB6ePZoZnGLwrd3B6Ru/agoHANTUBWpVNIdnM= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/monitoring v1.5.0/go.mod h1:/o9y8NYX5j91JjD/JvGLYbi86kL11OjyJXq2XziLJu4= -cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/spanner v1.28.0/go.mod h1:7m6mtQZn/hMbMfx62ct5EWrGND4DNqkXyrmBPRS+OJo= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo= -github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= -github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= -github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= -github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b h1:doCpXjVwui6HUN+xgNsNS3SZ0/jUZ68Eb+mJRNOZfog= -github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1831 h1:g7YHKEArwtJd4mynWxfzWCTMkqRzqa0QpuF2enx8WkQ= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1831/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= -github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.11/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.44.206 h1:xC7O40wdnKH4A95KdYt+smXl9hig1vu9b3mFxAxUoak= -github.com/aws/aws-sdk-go v1.44.206/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0= -github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 h1:RKci2D7tMwpvGpDNZnGQw9wk6v7o/xSwFcUAuNPoB8k= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9/go.mod h1:vCmV1q1VK8eoQJ5+aYE7PkK1K6v41qJ5pJdK3ggCDvg= -github.com/aws/aws-sdk-go-v2/config v1.6.0/go.mod h1:TNtBVmka80lRPk5+S9ZqVfFszOQAGJJ9KbT3EM3CHNU= -github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/config v1.17.10 h1:zBy5QQ/mkvHElM1rygHPAzuH+sl8nsdSaxSWj0+rpdE= -github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= -github.com/aws/aws-sdk-go-v2/credentials v1.3.2/go.mod h1:PACKuTJdt6AlXvEq8rFI4eDmoqDFC5DpVKQbWysaDgM= -github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23 h1:LctvcJMIb8pxvk5hQhChpCu0WlU6oKQmcYb1HA4IZSA= -github.com/aws/aws-sdk-go-v2/credentials v1.12.23/go.mod h1:0awX9iRr/+UO7OwRQFpV1hNtXxOVuehpjVEzrIAYNcA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.4.0/go.mod h1:Mj/U8OpDbcVcoctrYwA2bak8k/HFPdcLzI/vaiXMwuM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 h1:E3PXZSI3F2bzyj6XxUXdTIfvp425HHhwKsFvmzBwHgs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.4.0/go.mod h1:eHwXu2+uE/T6gpnYWwBwqoeqRf9IXyCcolyOWDRAErQ= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.4/go.mod h1:Ex7XQmbFmgFHrjUX6TN3mApKW5Hglyga+F7wZHTtYhA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.0/go.mod h1:Q5jATQc+f1MfZp3PDMhn6ry18hGvE0i8yvbXoKbnZaE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 h1:Mza+vlnZr+fPKFKRq/lKGVvM6B/8ZZmNdEopOwSQLms= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 h1:2EXB7dtGwRYIN3XQ9qwIW504DVbKIw3r89xQnonGdsQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16/go.mod h1:XH+3h395e3WVdd6T2Z3mPxuI+x/HVtdqVOREkTiyubs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.2.2/go.mod h1:EASdTcM1lGhUe1/p4gkojHwlGJkeoRjjr1sRCzup3Is= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 h1:dpiPHgmFstgkLG07KaYAewvuptq5kvo52xn7tVSrtrQ= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10/go.mod h1:9cBNUHI2aW4ho0A5T87O294iPDuuUOSIEDjnd1Lq/z0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 h1:KSvtm1+fPXE0swe9GPjc6msyrdTT0LB/BP8eLugL1FI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20/go.mod h1:Mp4XI/CkWGD79AQxZ5lIFlgvC0A+gl+4BmyG1F+SfNc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.2/go.mod h1:NXmNI41bdEsJMrD0v9rUvbGCB5GwdBEpKvUvIY3vTFg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 h1:GE25AWCdNUPh9AOJzI9KIJnja7IwUc1WyUqz/JTyJ/I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.5.2/go.mod h1:QuL2Ym8BkrLmN4lUofXYq6000/i5jPjosCNK//t6gak= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.2/go.mod h1:np7TMuJNT83O0oDOSF8i4dF3dvGqA6hPYYo6YYkzgRA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 h1:piDBAaWkaxkkVV3xJJbTehXCZRXYs49kvpi/LG6LR2o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19/go.mod h1:BmQWRVkLTmyNzYPFAZgon53qKLWBNSvonugD1MrSWUs= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.12.0/go.mod h1:6J++A5xpo7QDsIeSqPK4UHqMSyPOCopa+zKtqAMhqVQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.16.1/go.mod h1:CQe/KvWV1AqRc65KqeJjrLzr5X2ijnFTTVzJW0VBRCI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 h1:/EMdFPW/Ppieh0WUtQf1+qCGNLdsq5UWUyevBQ6vMVc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1/go.mod h1:/NHbqPRiwxSPVOB2Xr+StDEH+GWV/64WwnUjv4KYzV0= -github.com/aws/aws-sdk-go-v2/service/sso v1.3.2/go.mod h1:J21I6kF+d/6XHVk7kp/cx9YVD2TMD2TbLwtRGVcinXo= -github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 h1:GFZitO48N/7EsFDt8fMa5iYdmWqkUDDB3Eje6z3kbG0= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 h1:jcw6kKZrtNfBPJkaHrscDOZoe5gvi9wjudnxvozYFJo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= -github.com/aws/aws-sdk-go-v2/service/sts v1.6.1/go.mod h1:hLZ/AnkIKHLuPGjEiyghNEdvJ2PP0MgOxcmv9EBJ4xs= -github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 h1:KRAix/KHvjGODaHAMXnxRk9t0D+4IJVUuS/uwXxngXk= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.1/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= -github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/brave-intl/bat-go v1.0.2 h1:0JHIPQrs3PuPL0BdUBOdlEImN7IsSZ3J5pLHr2wPCxA= -github.com/brave-intl/bat-go v1.0.2/go.mod h1:v0+eoO+LrHWe9+gcunYvB+t5mXxPRY1RVR3ZoVKZuC4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= -github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/circonus-labs/circonusllhist v0.1.5 h1:ZSFhQTeulzPZW0V8reKXXQ3a2fa4Bld2X+aLUopckSw= -github.com/circonus-labs/circonusllhist v0.1.5/go.mod h1:qYdvAhMwBRcpDX02mwOjBk0EZltij0Q7ZmzZM0BzUaY= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhui/dktest v0.3.10 h1:0frpeeoM9pHouHjhLeZDuDTJ0PqjDTrycaHaMmkJAo8= -github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= -github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= -github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= -github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI= -github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= -github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= -github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 h1:wxgEEZvsnOTrDO2npSSKUMDx5IykfoGmro+/Vjc1BQ8= -github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= -github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc= -github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= -github.com/google/go-metrics-stackdriver v0.5.0 h1:2j/zXdGULmoTqEOcxfD05edfagkRRk7CAPiH8biQF3o= -github.com/google/go-metrics-stackdriver v0.5.0/go.mod h1:TSSNfaJwcAremxTHm6pe+uDwuT3u71QSyDwrRWAfPZg= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= -github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.5 h1:rOFDv+3k05mnW0oaDLffhVUwg03Csn0mvfO98Wdd2bE= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.5/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= -github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.4 h1:ws2CPDuXMKwaBb2z/duBCdnB9pSxlN2nuDZWXcVj6RU= -github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.4/go.mod h1:dDxt3GXi5QONVHYrJi2+EjsJLCUs59FktZQA8ZMnm+U= -github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 h1:ydUCtmr8f9F+mHZ1iCsvzqFTXqNVpewX3s9zcYipMKI= -github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1/go.mod h1:Sl/ffzV57UAyjtSg1h5Km0rN5+dtzZJm1CUztkoCW2c= -github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.1 h1:WxpTuafkDjdeeu0Xtk9y3m9YAJhfFMb8+y6eTnxvV8A= -github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.1/go.mod h1:3D5UB9fjot4oUTYGQ5gGmhLJKreyLZeI0XB+NxcLTKs= -github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.1 h1:6joKpqCFveaNMEwC3qna67usws6DjdxqfCuQEHSM0aM= -github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.1/go.mod h1:sDmsWR/W2LqwU217o32RzdHMb/FywGLF72PVIhpZ3hE= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.1 h1:+paf/3ompzaXe07BdxkV1vTnqvhwtmZPE4yQnMPTThI= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.1/go.mod h1:YRtkersQ2N3iHlPDG5B3xBQtBsNZ3bjmlCwnrl26jVE= -github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.0 h1:FnWV2E0NLj+yYdhToUQjU81ayCMgURiL2WbJ0V7u/XY= -github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.0/go.mod h1:17twrc0lM8IpfGqIv69WQvwgDiu3nRwWlk5YfCSQduY= -github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.1 h1:72zlIBTJd2pvYmINqotpvcI4ZXLxhRq2cVPTuqv0xqY= -github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.1/go.mod h1:JytRAxdJViV+unUUWedb7uzEy5pgu7OurbqX0eHEikE= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= -github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 h1:phcbL8urUzF/kxA/Oj6awENaRwfWsjP59GW7u2qlDyY= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= -github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/hcp-sdk-go v0.23.0 h1:3WarkQSK0VzxJaH6psHIGQagag3ujL+NjWagZZHpiZM= -github.com/hashicorp/hcp-sdk-go v0.23.0/go.mod h1:/9UoDY2FYYA8lFaKBb2HmM/jKYZGANmf65q9QRc/cVw= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault v1.12.7 h1:T+nWB2Ihe6xiNelLfC1BMJhV0dgJngDgRW8EiG6/em8= -github.com/hashicorp/vault v1.12.7/go.mod h1:TkP77qkpNyb7kXeZlLLsj0luGitsq5BzRtaBoXgSCs4= -github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI= -github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= -github.com/hashicorp/vault/sdk v0.6.1-0.20230427140652-b4b396ffc14f h1:0KmxboDYCgT0rssFOTOkqVkLGbueORiGpkfVA6r5LQs= -github.com/hashicorp/vault/sdk v0.6.1-0.20230427140652-b4b396ffc14f/go.mod h1:XduFY2J0HMoM4mt4kkxlrrkF8bYowzUc2Gog6epWVsA= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= -github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= -github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4= -github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= -github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA= -github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= -github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/oracle/oci-go-sdk/v60 v60.0.0 h1:EJAWjEi4SY5Raha6iUzq4LTQ0uM5YFw/wat/L1ehIEM= -github.com/oracle/oci-go-sdk/v60 v60.0.0/go.mod h1:krz+2gkSzlSL/L4PvP0Z9pZpag9HYLNtsMd1PmxlA2w= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= -github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= -github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5 h1:UOKr78K7smMjkKuwZjbOCzIFTUhIUzXc5+tYeaPRirE= -github.com/superp00t/niceware v0.0.0-20170614015008-16cb30c384b5/go.mod h1:asllDl3XCEQ1lFPD4Y1juAn5p35Zd4ghNGcDKN7U7dU= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA= -go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210818153620-00dd8d7831e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.79.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= -gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= -gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= -modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= -modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= -modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= -modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= -modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= -modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/tools/macaroon/cmd/.gitignore b/tools/macaroon/cmd/.gitignore deleted file mode 100644 index 336ed4957..000000000 --- a/tools/macaroon/cmd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -macaroon-cli-gen diff --git a/tools/macaroon/cmd/README.md b/tools/macaroon/cmd/README.md deleted file mode 100644 index e556dbb91..000000000 --- a/tools/macaroon/cmd/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Macaroon CLI Generation - -This tool provides a mechanism for creation of macaroon tokens from the command line -provided there is a yaml file defining all of the tokens one wishes to generate, as -well as the HMAC secret key. - -```bash -cd bat-go/ # go to root of bat-go repository -go build ./... # build the bat-go exec -./bat-go macaroon create --config cmd/macaroon/brave-talk/brave_talk_free_dev.yaml --secret secret -``` - -The above command will read "example.yaml" parse it into a token config and then create -macaroons based on the tokens specified in the yaml file. Below is an example yaml file: - -```yaml ---- -tokens: - - - id: "brave user-wallet-vote sku token v2" - version: 2 - location: "brave.com" - first_party_caveats: - - id: "ef0db4e2-c247-4b9b-99ab-5fc72213ac3a" - - sku: "user-wallet-vote" - - - id: "brave anon-card-vote sku token v2" - version: 2 - location: "brave.com" - first_party_caveats: - - id: "5cbe9620-ca98-4d09-8c18-1b8582d78e60" - - sku: "anon-card-vote" -``` diff --git a/tools/macaroon/cmd/ac_skus.yaml b/tools/macaroon/cmd/ac_skus.yaml deleted file mode 100644 index b17aa0fad..000000000 --- a/tools/macaroon/cmd/ac_skus.yaml +++ /dev/null @@ -1,20 +0,0 @@ -tokens: - - id: "brave user-wallet-vote sku token v1" - version: 2 - location: "brave.com" - first_party_caveats: - - sku: "user-wallet-vote" - - price: 0.25 - - currency: "BAT" - - description: "" - - credential_type: "single-use" - - - id: "brave anon-card-vote sku token v1" - version: 2 - location: "brave.com" - first_party_caveats: - - sku: "anon-card-vote" - - price: 0.25 - - currency: "BAT" - - description: "" - - credential_type: "single-use" diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time-limited.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time-limited.yaml deleted file mode 100644 index 2c90ab781..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time-limited.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tokens: - - id: "brave-firewall-vpn-premium sku token v1" - version: 1 - location: "vpn.brave.software" - first_party_caveats: - - sku: "brave-firewall-vpn-premium" - - price: 9.99 - - currency: "USD" - - description: "Brave Firewall + VPN" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_K1c8W3oM4mUsGw", - "stripe_item_id": "price_1JNYuNHof20bphG6BvgeYEnt", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2.yaml deleted file mode 100644 index 38b94da06..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-vpn-premium" - version: 1 - location: "vpn.brave.software" - first_party_caveats: - - sku: "brave-firewall-vpn-premium" - - price: 9.99 - - currency: "USD" - - description: "Brave Firewall + VPN" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "31" - - issuer_token_overlap: "2" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_K1c8W3oM4mUsGw", - "stripe_item_id": "price_1JNYuNHof20bphG6BvgeYEnt", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2_bat.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2_bat.yaml deleted file mode 100644 index 4f3a48b8b..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_dev_time_limited_v2_bat.yaml +++ /dev/null @@ -1,20 +0,0 @@ -tokens: - - id: "brave-vpn-premium" - version: 1 - location: "vpn.brave.software" - first_party_caveats: - - sku: "brave-firewall-vpn-premium" - - price: 15 - - currency: "BAT" - - description: "Brave Firewall + VPN" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "31" - - issuer_token_overlap: "2" - - allowed_payment_methods: "radom" - - metadata: ' -{ - "radom_success_uri": "https://account.brave.software/account/?intent=provision", - "radom_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time-limited.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time-limited.yaml deleted file mode 100644 index 5bca4140b..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time-limited.yaml +++ /dev/null @@ -1,20 +0,0 @@ -tokens: - - id: "brave-vpn-premium sku token v1" - version: 1 - location: "vpn.brave.com" - first_party_caveats: - - sku: "brave-vpn-premium" - - price: 9.99 - - currency: "USD" - - description: "Brave VPN" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Lhv8qsPsn6WHrx", - "stripe_item_id": "price_1L0VHmBSm1mtrN9nT5DPmUZb", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' - diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2.yaml deleted file mode 100644 index d427bbaaa..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2.yaml +++ /dev/null @@ -1,24 +0,0 @@ -tokens: - - id: "brave-vpn-premium" - version: 1 - location: "vpn.brave.com" - first_party_caveats: - - sku: "brave-vpn-premium" - - price: 9.99 - - currency: "USD" - - description: "brave-vpn-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - expires_after: "P1M" - - issuer_token_buffer: "31" - - issuer_token_overlap: "2" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Lhv8qsPsn6WHrx", - "stripe_item_id": "price_1L0VHmBSm1mtrN9nT5DPmUZb", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' - diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2_bat.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2_bat.yaml deleted file mode 100644 index 0af22ef96..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_prod_time_limited_v2_bat.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-vpn-premium" - version: 1 - location: "vpn.brave.com" - first_party_caveats: - - sku: "brave-vpn-premium" - - price: 15 - - currency: "BAT" - - description: "brave-vpn-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - expires_after: "P1M" - - issuer_token_buffer: "31" - - issuer_token_overlap: "2" - - allowed_payment_methods: "radom" - - metadata: ' -{ - "radom_success_uri": "https://account.brave.com/account/?intent=provision", - "radom_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' - diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time-limited.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time-limited.yaml deleted file mode 100644 index dca658d3b..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time-limited.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tokens: - - id: "brave-firewall-vpn-premium sku token v1" - version: 1 - location: "vpn.bravesoftware.com" - first_party_caveats: - - sku: "brave-vpn-premium" - - price: 9.99 - - currency: "USD" - - description: "Brave VPN" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Lhv4OM1aAPxflY", - "stripe_item_id": "price_1L0VEhBSm1mtrN9nGB4kZkfh", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2.yaml deleted file mode 100644 index 5f3a9713b..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-vpn-premium" - version: 1 - location: "vpn.bravesoftware.com" - first_party_caveats: - - sku: "brave-vpn-premium" - - price: 9.99 - - currency: "USD" - - description: "brave-vpn-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "31" - - issuer_token_overlap: "2" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Lhv4OM1aAPxflY", - "stripe_item_id": "price_1L0VEhBSm1mtrN9nGB4kZkfh", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2_bat.yaml b/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2_bat.yaml deleted file mode 100644 index 494352528..000000000 --- a/tools/macaroon/cmd/brave-firewall-vpn/premium_stg_time_limited_v2_bat.yaml +++ /dev/null @@ -1,20 +0,0 @@ -tokens: - - id: "brave-vpn-premium" - version: 1 - location: "vpn.bravesoftware.com" - first_party_caveats: - - sku: "brave-vpn-premium" - - price: 15 - - currency: "BAT" - - description: "brave-vpn-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "31" - - issuer_token_overlap: "2" - - allowed_payment_methods: "radom" - - metadata: ' -{ - "radom_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "radom_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-leo/premium_development_time_limited_v2.yaml b/tools/macaroon/cmd/brave-leo/premium_development_time_limited_v2.yaml deleted file mode 100644 index a9d94d872..000000000 --- a/tools/macaroon/cmd/brave-leo/premium_development_time_limited_v2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-leo-premium" - version: 1 - location: "leo.brave.software" - first_party_caveats: - - sku: "brave-leo-premium" - - price: 15.00 - - currency: "USD" - - description: "brave-leo-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "3" - - issuer_token_overlap: "0" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_OtZCXOCIO3AJE6", - "stripe_item_id": "price_1O5m3lHof20bphG6DloANAcc", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-leo/premium_development_yearly_time_limited_v2.yaml b/tools/macaroon/cmd/brave-leo/premium_development_yearly_time_limited_v2.yaml deleted file mode 100644 index feb71c0a2..000000000 --- a/tools/macaroon/cmd/brave-leo/premium_development_yearly_time_limited_v2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-leo-premium-year" - version: 1 - location: "leo.brave.software" - first_party_caveats: - - sku: "brave-leo-premium-year" - - price: 135.00 - - currency: "USD" - - description: "brave-leo-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1Y" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "3" - - issuer_token_overlap: "0" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_OtZCXOCIO3AJE6", - "stripe_item_id": "price_1O6re8Hof20bphG6tqdNEEAp", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-leo/premium_prod_time_limited_v2.yaml b/tools/macaroon/cmd/brave-leo/premium_prod_time_limited_v2.yaml deleted file mode 100644 index 4635023d3..000000000 --- a/tools/macaroon/cmd/brave-leo/premium_prod_time_limited_v2.yaml +++ /dev/null @@ -1,23 +0,0 @@ -tokens: - - id: "brave-leo-premium" - version: 1 - location: "leo.brave.com" - first_party_caveats: - - sku: "brave-leo-premium" - - price: 15.00 - - currency: "USD" - - description: "brave-leo-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - expires_after: "P1M" - - issuer_token_buffer: "3" - - issuer_token_overlap: "0" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_O9uKDYsRPXNgfB", - "stripe_item_id": "price_1NXmj0BSm1mtrN9nF0elIhiq", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-leo/premium_prod_yearly_time_limited_v2.yaml b/tools/macaroon/cmd/brave-leo/premium_prod_yearly_time_limited_v2.yaml deleted file mode 100644 index 2bc08a18a..000000000 --- a/tools/macaroon/cmd/brave-leo/premium_prod_yearly_time_limited_v2.yaml +++ /dev/null @@ -1,23 +0,0 @@ -tokens: - - id: "brave-leo-premium-year" - version: 1 - location: "leo.brave.com" - first_party_caveats: - - sku: "brave-leo-premium-year" - - price: 135.00 - - currency: "USD" - - description: "brave-leo-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1Y" - - each_credential_valid_duration: "P1D" - - expires_after: "P1M" - - issuer_token_buffer: "3" - - issuer_token_overlap: "0" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_O9uKDYsRPXNgfB", - "stripe_item_id": "price_1NXmfTBSm1mtrN9nybnyolId", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-leo/premium_stg_time_limited_v2.yaml b/tools/macaroon/cmd/brave-leo/premium_stg_time_limited_v2.yaml deleted file mode 100644 index 79a068131..000000000 --- a/tools/macaroon/cmd/brave-leo/premium_stg_time_limited_v2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-leo-premium" - version: 1 - location: "leo.bravesoftware.com" - first_party_caveats: - - sku: "brave-leo-premium" - - price: 15.00 - - currency: "USD" - - description: "brave-leo-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1M" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "3" - - issuer_token_overlap: "0" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_OKRYJ77wYOk771", - "stripe_item_id": "price_1NXmfTBSm1mtrN9nYjSNMs4X", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-leo/premium_stg_yearly_time_limited_v2.yaml b/tools/macaroon/cmd/brave-leo/premium_stg_yearly_time_limited_v2.yaml deleted file mode 100644 index 65403b1eb..000000000 --- a/tools/macaroon/cmd/brave-leo/premium_stg_yearly_time_limited_v2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-leo-premium-year" - version: 1 - location: "leo.bravesoftware.com" - first_party_caveats: - - sku: "brave-leo-premium-year" - - price: 135.00 - - currency: "USD" - - description: "brave-leo-premium" - - credential_type: "time-limited-v2" - - credential_valid_duration: "P1Y" - - each_credential_valid_duration: "P1D" - - issuer_token_buffer: "3" - - issuer_token_overlap: "0" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_OKRYJ77wYOk771", - "stripe_item_id": "price_1NXmfTBSm1mtrN9nybnyolId", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-search/development-captcha-single-use.yaml b/tools/macaroon/cmd/brave-search/development-captcha-single-use.yaml deleted file mode 100644 index c913a4b72..000000000 --- a/tools/macaroon/cmd/brave-search/development-captcha-single-use.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tokens: - - id: "brave search captcha sku token v1 - development" - version: 2 - location: "search.brave.software" - first_party_caveats: - - sku: "brave-search-captcha" - - price: 0 - - currency: "BAT" - - description: "single use captcha sku" - - credential_type: "single-use" diff --git a/tools/macaroon/cmd/brave-search/development-captcha-time-limited.yaml b/tools/macaroon/cmd/brave-search/development-captcha-time-limited.yaml deleted file mode 100644 index 758e91dc6..000000000 --- a/tools/macaroon/cmd/brave-search/development-captcha-time-limited.yaml +++ /dev/null @@ -1,12 +0,0 @@ -tokens: - - id: "brave search captcha sku token v1 - development" - version: 2 - location: "search.brave.software" - first_party_caveats: - - sku: "brave-search-captcha" - - price: 0 - - currency: "BAT" - - description: "single use captcha sku" - - credential_type: "time-limited" - - credential_valid_duration: "P1D" - - issuance_interval: "PT1H" diff --git a/tools/macaroon/cmd/brave-search/premium_dev_time-limited.yaml b/tools/macaroon/cmd/brave-search/premium_dev_time-limited.yaml deleted file mode 100644 index 2d4f05108..000000000 --- a/tools/macaroon/cmd/brave-search/premium_dev_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-search-premium sku token v1" - version: 1 - location: "search.brave.software" - first_party_caveats: - - sku: "brave-search-premium" - - price: 3.00 - - currency: "USD" - - description: "Premium access to Brave Search" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - issuance_interval: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_JzSevyZM5iBSrf", - "stripe_item_id": "price_1JLTjHHof20bphG60WYcPcgk", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-search/premium_prod_time-limited.yaml b/tools/macaroon/cmd/brave-search/premium_prod_time-limited.yaml deleted file mode 100644 index 4a5d84a84..000000000 --- a/tools/macaroon/cmd/brave-search/premium_prod_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-search-premium sku token v1" - version: 1 - location: "search.brave.com" - first_party_caveats: - - sku: "brave-search-premium" - - price: 3.00 - - currency: "USD" - - description: "Premium access to Brave Search" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - issuance_interval: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_KTlyzecsq7evk6", - "stripe_item_id": "price_1JooQnBSm1mtrN9nn2OMKpjj", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-search/premium_stg_time-limited.yaml b/tools/macaroon/cmd/brave-search/premium_stg_time-limited.yaml deleted file mode 100644 index 463b756dc..000000000 --- a/tools/macaroon/cmd/brave-search/premium_stg_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-search-premium sku token v1" - version: 1 - location: "search.bravesoftware.com" - first_party_caveats: - - sku: "brave-search-premium" - - price: 3.00 - - currency: "USD" - - description: "Premium access to Brave Search" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - issuance_interval: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_KTm6JaZssANPBv", - "stripe_item_id": "price_1JooXrBSm1mtrN9n3mRILeXM", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-search/premium_year_dev_time-limited.yaml b/tools/macaroon/cmd/brave-search/premium_year_dev_time-limited.yaml deleted file mode 100644 index fb2a1ebf1..000000000 --- a/tools/macaroon/cmd/brave-search/premium_year_dev_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-search-premium-year sku token v1" - version: 1 - location: "search.brave.software" - first_party_caveats: - - sku: "brave-search-adfree" - - price: 30.00 - - currency: "USD" - - description: "Premium access to Brave Search" - - credential_type: "time-limited" - - credential_valid_duration: "P1Y" - - issuance_interval: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_JzSevyZM5iBSrf", - "stripe_item_id": "price_1JoXvFHof20bphG6yH6kQiPG", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-search/premium_year_prod_time-limited.yaml b/tools/macaroon/cmd/brave-search/premium_year_prod_time-limited.yaml deleted file mode 100644 index fc0f96a78..000000000 --- a/tools/macaroon/cmd/brave-search/premium_year_prod_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-search-premium sku token v1" - version: 1 - location: "search.brave.com" - first_party_caveats: - - sku: "brave-search-premium" - - price: 30.00 - - currency: "USD" - - description: "Premium access to Brave Search" - - credential_type: "time-limited" - - credential_valid_duration: "P1Y" - - issuance_interval: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_KTlyzecsq7evk6", - "stripe_item_id": "price_1JooR8BSm1mtrN9nmc2vbT07", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-search/premium_year_stg_time-limited.yaml b/tools/macaroon/cmd/brave-search/premium_year_stg_time-limited.yaml deleted file mode 100644 index 91909a9a4..000000000 --- a/tools/macaroon/cmd/brave-search/premium_year_stg_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-search-premium sku token v1" - version: 1 - location: "search.bravesoftware.com" - first_party_caveats: - - sku: "brave-search-premium" - - price: 30.00 - - currency: "USD" - - description: "Premium access to Brave Search" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - issuance_interval: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_KTm6JaZssANPBv", - "stripe_item_id": "price_1JooYqBSm1mtrN9nxUBzrFpl", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-search/production-captcha-single-use.yaml b/tools/macaroon/cmd/brave-search/production-captcha-single-use.yaml deleted file mode 100644 index cbc17cc08..000000000 --- a/tools/macaroon/cmd/brave-search/production-captcha-single-use.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tokens: - - id: "brave search captcha sku token v1 - productoin" - version: 2 - location: "search.brave.com" - first_party_caveats: - - sku: "brave-search-captcha" - - price: 0 - - currency: "BAT" - - description: "single use captcha sku" - - credential_type: "single-use" diff --git a/tools/macaroon/cmd/brave-search/staging-captcha-single-use.yaml b/tools/macaroon/cmd/brave-search/staging-captcha-single-use.yaml deleted file mode 100644 index 7a9006a72..000000000 --- a/tools/macaroon/cmd/brave-search/staging-captcha-single-use.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tokens: - - id: "brave search captcha sku token v1 - staging" - version: 2 - location: "search.bravesoftware.com" - first_party_caveats: - - sku: "brave-search-captcha" - - price: 0 - - currency: "BAT" - - description: "single use captcha sku" - - credential_type: "single-use" diff --git a/tools/macaroon/cmd/brave-skus/free_1m_tlv2.yaml b/tools/macaroon/cmd/brave-skus/free_1m_tlv2.yaml deleted file mode 100644 index ff461808e..000000000 --- a/tools/macaroon/cmd/brave-skus/free_1m_tlv2.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-free-1m-tlv2 sku token v1" - version: 1 - location: "premiumfreetrial.bravesoftware.com" - first_party_caveats: - - sku: "brave-free-1m-tlv2" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave premium products" - - credential_type: "time-limited-v2" - - credential_valid_duration: "PT60S" - - issuer_token_buffer: "30" - - issuer_token_overlap: "1" diff --git a/tools/macaroon/cmd/brave-skus/free_5m_tlv2.yaml b/tools/macaroon/cmd/brave-skus/free_5m_tlv2.yaml deleted file mode 100644 index 74edc9edb..000000000 --- a/tools/macaroon/cmd/brave-skus/free_5m_tlv2.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-free-5m-tlv2 sku token v1" - version: 1 - location: "premiumfreetrial.bravesoftware.com" - first_party_caveats: - - sku: "brave-free-5m-tlv2" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave premium products" - - credential_type: "time-limited-v2" - - credential_valid_duration: "PT300S" - - issuer_token_buffer: "30" - - issuer_token_overlap: "1" diff --git a/tools/macaroon/cmd/brave-skus/free_dev.yaml b/tools/macaroon/cmd/brave-skus/free_dev.yaml deleted file mode 100644 index f84b0c05a..000000000 --- a/tools/macaroon/cmd/brave-skus/free_dev.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-free sku token v1" - version: 1 - location: "freetrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "" diff --git a/tools/macaroon/cmd/brave-skus/free_nocc_dev.yaml b/tools/macaroon/cmd/brave-skus/free_nocc_dev.yaml deleted file mode 100644 index 13faa0592..000000000 --- a/tools/macaroon/cmd/brave-skus/free_nocc_dev.yaml +++ /dev/null @@ -1,9 +0,0 @@ -tokens: - - id: "brave-free-nocc sku token v1" - version: 1 - location: "freenocctrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free-nocc" - - description: "Free trial (no cc) access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" diff --git a/tools/macaroon/cmd/brave-skus/free_nocc_stg.yaml b/tools/macaroon/cmd/brave-skus/free_nocc_stg.yaml deleted file mode 100644 index 13faa0592..000000000 --- a/tools/macaroon/cmd/brave-skus/free_nocc_stg.yaml +++ /dev/null @@ -1,9 +0,0 @@ -tokens: - - id: "brave-free-nocc sku token v1" - version: 1 - location: "freenocctrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free-nocc" - - description: "Free trial (no cc) access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" diff --git a/tools/macaroon/cmd/brave-skus/free_premium_dev.yaml b/tools/macaroon/cmd/brave-skus/free_premium_dev.yaml deleted file mode 100644 index 5c1085a3f..000000000 --- a/tools/macaroon/cmd/brave-skus/free_premium_dev.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-free-premium-trial sku token v1" - version: 1 - location: "premiumfreetrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free-premium-trial" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave premium products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "price_1IqgVpHof20bphG6wcCByrRf" diff --git a/tools/macaroon/cmd/brave-skus/free_premium_stg.yaml b/tools/macaroon/cmd/brave-skus/free_premium_stg.yaml deleted file mode 100644 index 5c1085a3f..000000000 --- a/tools/macaroon/cmd/brave-skus/free_premium_stg.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-free-premium-trial sku token v1" - version: 1 - location: "premiumfreetrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free-premium-trial" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave premium products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "price_1IqgVpHof20bphG6wcCByrRf" diff --git a/tools/macaroon/cmd/brave-skus/free_stg.yaml b/tools/macaroon/cmd/brave-skus/free_stg.yaml deleted file mode 100644 index f84b0c05a..000000000 --- a/tools/macaroon/cmd/brave-skus/free_stg.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-free sku token v1" - version: 1 - location: "freetrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "" diff --git a/tools/macaroon/cmd/brave-skus/free_unlimited_dev.yaml b/tools/macaroon/cmd/brave-skus/free_unlimited_dev.yaml deleted file mode 100644 index fa5bb1b79..000000000 --- a/tools/macaroon/cmd/brave-skus/free_unlimited_dev.yaml +++ /dev/null @@ -1,14 +0,0 @@ -tokens: - - id: "brave-free-unlimited-trial sku token v1" - version: 1 - location: "unlimitedfreetrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free-unlimited-trial" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "" - - stripe_product_id: "price_1IqgWmHof20bphG6CGZjzw7U" diff --git a/tools/macaroon/cmd/brave-skus/free_unlimitted_stg.yaml b/tools/macaroon/cmd/brave-skus/free_unlimitted_stg.yaml deleted file mode 100644 index fa5bb1b79..000000000 --- a/tools/macaroon/cmd/brave-skus/free_unlimitted_stg.yaml +++ /dev/null @@ -1,14 +0,0 @@ -tokens: - - id: "brave-free-unlimited-trial sku token v1" - version: 1 - location: "unlimitedfreetrial.bsg.brave.software" - first_party_caveats: - - sku: "brave-free-unlimited-trial" - - price: 0 - - currency: "USD" - - description: "Free trial access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "" - - stripe_product_id: "price_1IqgWmHof20bphG6CGZjzw7U" diff --git a/tools/macaroon/cmd/brave-skus/premium_dev.yaml b/tools/macaroon/cmd/brave-skus/premium_dev.yaml deleted file mode 100644 index 792622213..000000000 --- a/tools/macaroon/cmd/brave-skus/premium_dev.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-premium sku token v1" - version: 1 - location: "premium.bsg.brave.software" - first_party_caveats: - - sku: "brave-premium" - - price: 10 - - currency: "USD" - - description: "Premium access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "price_1IqgVpHof20bphG6wcCByrRf" diff --git a/tools/macaroon/cmd/brave-skus/premium_stg.yaml b/tools/macaroon/cmd/brave-skus/premium_stg.yaml deleted file mode 100644 index 792622213..000000000 --- a/tools/macaroon/cmd/brave-skus/premium_stg.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-premium sku token v1" - version: 1 - location: "premium.bsg.brave.software" - first_party_caveats: - - sku: "brave-premium" - - price: 10 - - currency: "USD" - - description: "Premium access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "price_1IqgVpHof20bphG6wcCByrRf" diff --git a/tools/macaroon/cmd/brave-skus/unlimited_dev.yaml b/tools/macaroon/cmd/brave-skus/unlimited_dev.yaml deleted file mode 100644 index 37321254f..000000000 --- a/tools/macaroon/cmd/brave-skus/unlimited_dev.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-unlimited sku token v1" - version: 1 - location: "unlimited.bsg.brave.software" - first_party_caveats: - - sku: "brave-unlimited" - - price: 15 - - currency: "USD" - - description: "Unlimited access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "price_1IqgWmHof20bphG6CGZjzw7U" diff --git a/tools/macaroon/cmd/brave-skus/unlimited_stg.yaml b/tools/macaroon/cmd/brave-skus/unlimited_stg.yaml deleted file mode 100644 index 37321254f..000000000 --- a/tools/macaroon/cmd/brave-skus/unlimited_stg.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-unlimited sku token v1" - version: 1 - location: "unlimited.bsg.brave.software" - first_party_caveats: - - sku: "brave-unlimited" - - price: 15 - - currency: "USD" - - description: "Unlimited access to Brave products" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] - - stripe_product_id: "price_1IqgWmHof20bphG6CGZjzw7U" diff --git a/tools/macaroon/cmd/brave-talk/brave_talk_free_dev.yaml b/tools/macaroon/cmd/brave-talk/brave_talk_free_dev.yaml deleted file mode 100644 index 92d52c3f4..000000000 --- a/tools/macaroon/cmd/brave-talk/brave_talk_free_dev.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tokens: - - id: "brave-talk-free sku token v1" - version: 1 - location: "together.bsg.brave.software" - first_party_caveats: - - sku: "brave-talk-free" - - price: 0 - - currency: "BAT" - - description: "One month free trial for Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - diff --git a/tools/macaroon/cmd/brave-talk/brave_talk_paid_dev.yaml b/tools/macaroon/cmd/brave-talk/brave_talk_paid_dev.yaml deleted file mode 100644 index 76398145c..000000000 --- a/tools/macaroon/cmd/brave-talk/brave_talk_paid_dev.yaml +++ /dev/null @@ -1,18 +0,0 @@ -tokens: - - id: "brave-talk-paid sku token v1" - version: 1 - location: "together.bsg.brave.software" - first_party_caveats: - - sku: "brave-talk-paid" - - price: 5 - - currency: "USD" - - description: "One month paid subscription for Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: "stripe" - - metadata: > - { - "stripe_product_id":"price_1Hpg8nHof20bphG6X4eQ6Dit", - "stripe_success_uri": "https://together.bsg.brave.software/", - "stripe_cancel_uri": "https://together.bsg.brave.software/", - } diff --git a/tools/macaroon/cmd/brave-talk/premium_dev_time-limited.yaml b/tools/macaroon/cmd/brave-talk/premium_dev_time-limited.yaml deleted file mode 100644 index 15afca4a1..000000000 --- a/tools/macaroon/cmd/brave-talk/premium_dev_time-limited.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tokens: - - id: "brave-talk-premium sku token v1" - version: 1 - location: "talk.brave.software" - first_party_caveats: - - sku: "brave-talk-premium" - - price: 7.00 - - currency: "USD" - - description: "Premium access to Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Jlc224hFvAMvEp", - "stripe_item_id": "price_1J84oMHof20bphG6NBAT2vor", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-talk/premium_dev_time-limited_beta.yaml b/tools/macaroon/cmd/brave-talk/premium_dev_time-limited_beta.yaml deleted file mode 100644 index 17945fbff..000000000 --- a/tools/macaroon/cmd/brave-talk/premium_dev_time-limited_beta.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tokens: - - id: "brave-talk-premium sku token v1" - version: 1 - location: "talk-beta.brave.software" - first_party_caveats: - - sku: "brave-talk-premium" - - price: 7.00 - - currency: "USD" - - description: "Premium access to Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Jlc224hFvAMvEp", - "stripe_item_id": "price_1J84oMHof20bphG6NBAT2vor", - "stripe_success_uri": "https://account.brave.software/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.software/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-talk/premium_prod_time-limited.yaml b/tools/macaroon/cmd/brave-talk/premium_prod_time-limited.yaml deleted file mode 100644 index 071268bd9..000000000 --- a/tools/macaroon/cmd/brave-talk/premium_prod_time-limited.yaml +++ /dev/null @@ -1,19 +0,0 @@ -tokens: - - id: "brave-talk-premium-prod time limited sku token v1" - version: 1 - location: "talk.brave.com" - first_party_caveats: - - sku: "brave-talk-premium" - - price: 7.00 - - currency: "USD" - - description: "Premium access to Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Jw4zQxdHkpxSOe", - "stripe_item_id": "price_1JICpEBSm1mtrN9nwCKvpYQ4", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-talk/premium_prod_time-limited_beta.yaml b/tools/macaroon/cmd/brave-talk/premium_prod_time-limited_beta.yaml deleted file mode 100644 index 5c47d746b..000000000 --- a/tools/macaroon/cmd/brave-talk/premium_prod_time-limited_beta.yaml +++ /dev/null @@ -1,19 +0,0 @@ -tokens: - - id: "brave-talk-premium-prod time limited sku token v1" - version: 1 - location: "talk-beta.brave.com" - first_party_caveats: - - sku: "brave-talk-premium" - - price: 7.00 - - currency: "USD" - - description: "Premium access to Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Jw4zQxdHkpxSOe", - "stripe_item_id": "price_1JICpEBSm1mtrN9nwCKvpYQ4", - "stripe_success_uri": "https://account.brave.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.brave.com/plans/?intent=checkout" -}' diff --git a/tools/macaroon/cmd/brave-talk/premium_stg_time-limited.yaml b/tools/macaroon/cmd/brave-talk/premium_stg_time-limited.yaml deleted file mode 100644 index 0da8e255a..000000000 --- a/tools/macaroon/cmd/brave-talk/premium_stg_time-limited.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tokens: - - id: "brave-talk-premium sku token v1" - version: 1 - location: "talk.bravesoftware.com" - first_party_caveats: - - sku: "brave-talk-premium" - - price: 7.00 - - currency: "USD" - - description: "Premium access to Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - issuance_interval: "P1D" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_KTm4FtcniuTAOb", - "stripe_item_id": "price_1JooWTBSm1mtrN9ng3CpG4m4", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-talk/premium_stg_time-limited_beta.yaml b/tools/macaroon/cmd/brave-talk/premium_stg_time-limited_beta.yaml deleted file mode 100644 index 626f308a9..000000000 --- a/tools/macaroon/cmd/brave-talk/premium_stg_time-limited_beta.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tokens: - - id: "brave-talk-premium sku token v1" - version: 1 - location: "talk-beta.bravesoftware.com" - first_party_caveats: - - sku: "brave-talk-premium" - - price: 7.00 - - currency: "USD" - - description: "Premium access to Brave Talk" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - allowed_payment_methods: "stripe" - - metadata: ' -{ - "stripe_product_id": "prod_Jlc224hFvAMvEp", - "stripe_item_id": "price_1J84oMHof20bphG6NBAT2vor", - "stripe_success_uri": "https://account.bravesoftware.com/account/?intent=provision", - "stripe_cancel_uri": "https://account.bravesoftware.com/plans/?intent=checkout" -}' - - diff --git a/tools/macaroon/cmd/brave-together/brave_together_free_dev.yaml b/tools/macaroon/cmd/brave-together/brave_together_free_dev.yaml deleted file mode 100644 index 635da3de8..000000000 --- a/tools/macaroon/cmd/brave-together/brave_together_free_dev.yaml +++ /dev/null @@ -1,11 +0,0 @@ -tokens: - - id: "brave-together-free sku token v1" - version: 1 - location: "together.bsg.brave.software" - first_party_caveats: - - sku: "brave-together-free" - - price: 0 - - currency: "BAT" - - description: "One month free trial for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" diff --git a/tools/macaroon/cmd/brave-together/brave_together_free_prod.yaml b/tools/macaroon/cmd/brave-together/brave_together_free_prod.yaml deleted file mode 100644 index e757fc820..000000000 --- a/tools/macaroon/cmd/brave-together/brave_together_free_prod.yaml +++ /dev/null @@ -1,11 +0,0 @@ -tokens: - - id: "brave-together-free sku token v1" - version: 1 - location: "together.brave.software" - first_party_caveats: - - sku: "brave-together-free" - - price: 0 - - currency: "BAT" - - description: "One month free trial for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" diff --git a/tools/macaroon/cmd/brave-together/brave_together_free_staging.yaml b/tools/macaroon/cmd/brave-together/brave_together_free_staging.yaml deleted file mode 100644 index 16bb10718..000000000 --- a/tools/macaroon/cmd/brave-together/brave_together_free_staging.yaml +++ /dev/null @@ -1,11 +0,0 @@ -tokens: - - id: "brave-together-free sku token v1" - version: 1 - location: "together.bravesoftware.com" - first_party_caveats: - - sku: "brave-together-free" - - price: 0 - - currency: "BAT" - - description: "One month free trial for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" diff --git a/tools/macaroon/cmd/brave-together/brave_together_paid_dev.yaml b/tools/macaroon/cmd/brave-together/brave_together_paid_dev.yaml deleted file mode 100644 index f7a302c17..000000000 --- a/tools/macaroon/cmd/brave-together/brave_together_paid_dev.yaml +++ /dev/null @@ -1,12 +0,0 @@ -tokens: - - id: "brave-together-paid sku token v1" - version: 1 - location: "together.bsg.brave.software" - first_party_caveats: - - sku: "brave-together-paid" - - price: 5 - - currency: "USD" - - description: "One month paid subscription for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] \ No newline at end of file diff --git a/tools/macaroon/cmd/brave-together/brave_together_paid_prod.yaml b/tools/macaroon/cmd/brave-together/brave_together_paid_prod.yaml deleted file mode 100644 index f9656daef..000000000 --- a/tools/macaroon/cmd/brave-together/brave_together_paid_prod.yaml +++ /dev/null @@ -1,12 +0,0 @@ -tokens: - - id: "brave-together-paid sku token v1" - version: 1 - location: "together.brave.com" - first_party_caveats: - - sku: "brave-together-paid" - - price: 5 - - currency: "USD" - - description: "One month paid subscription for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] \ No newline at end of file diff --git a/tools/macaroon/cmd/brave-together/brave_together_paid_staging.yaml b/tools/macaroon/cmd/brave-together/brave_together_paid_staging.yaml deleted file mode 100644 index 49b47c321..000000000 --- a/tools/macaroon/cmd/brave-together/brave_together_paid_staging.yaml +++ /dev/null @@ -1,12 +0,0 @@ -tokens: - - id: "brave-together-paid sku token v1" - version: 1 - location: "together.brave.software" - first_party_caveats: - - sku: "brave-together-paid" - - price: 5 - - currency: "USD" - - description: "One month paid subscription for Brave Together" - - credential_type: "time-limited" - - credential_valid_duration: "P1M" - - payment_methods: ["stripe"] \ No newline at end of file diff --git a/tools/macaroon/cmd/example.yaml b/tools/macaroon/cmd/example.yaml deleted file mode 100644 index 6d5ecc4e4..000000000 --- a/tools/macaroon/cmd/example.yaml +++ /dev/null @@ -1,30 +0,0 @@ ---- -tokens: - # - - # id: "brave user-wallet-vote sku token v1" - # version: 1 - # location: "brave.com" - # first_party_caveats: - # - id: "ef0db4e2-c247-4b9b-99ab-5fc72213ac3a" - # - sku: "user-wallet-vote" - # - - # id: "brave anon-card-vote sku token v1" - # version: 1 - # location: "brave.com" - # first_party_caveats: - # - id: "5cbe9620-ca98-4d09-8c18-1b8582d78e60" - # - sku: "anon-card-vote" - - - id: "brave user-wallet-vote sku token v2" - version: 2 - location: "brave.com" - first_party_caveats: - - id: "ef0db4e2-c247-4b9b-99ab-5fc72213ac3a" - - sku: "user-wallet-vote" - - - id: "brave anon-card-vote sku token v2" - version: 2 - location: "brave.com" - first_party_caveats: - - id: "5cbe9620-ca98-4d09-8c18-1b8582d78e60" - - sku: "anon-card-vote" diff --git a/tools/macaroon/cmd/free_example.yaml b/tools/macaroon/cmd/free_example.yaml deleted file mode 100644 index 52c8793ac..000000000 --- a/tools/macaroon/cmd/free_example.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tokens: - - id: "integration test free sku token" - version: 2 - location: "brave.com" - first_party_caveats: - - sku: "integration-test-free" - - price: 0.00 - - currency: "BAT" - - description: "" - - credential_type: "single-use" diff --git a/tools/macaroon/cmd/gen.go b/tools/macaroon/cmd/gen.go deleted file mode 100644 index 87673d84d..000000000 --- a/tools/macaroon/cmd/gen.go +++ /dev/null @@ -1,100 +0,0 @@ -package macaroon - -import ( - "context" - "fmt" - - "github.com/brave-intl/bat-go/cmd" - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - // MacaroonCmd is a subcommand for macaroons - MacaroonCmd = &cobra.Command{ - Use: "macaroon", - Short: "macaroon subcommand", - } - // MacaroonCreateCmd generates a macaroon - MacaroonCreateCmd = &cobra.Command{ - Use: "create", - Short: "create a macaroon", - Run: cmd.Perform("macaroon creation", RunMacaroonCreate), - } -) - -func init() { - MacaroonCmd.AddCommand( - MacaroonCreateCmd, - ) - cmd.RootCmd.AddCommand( - MacaroonCmd, - ) - - createBuilder := cmdutils.NewFlagBuilder(MacaroonCreateCmd) - - createBuilder.Flag().String("config", "example.yaml", - "the location of the config file"). - Bind("config") - - createBuilder.Flag().String("secret", "mysecret", - "the value of the secret"). - Env("MACAROON_SECRET"). - Bind("secret") -} - -// RunMacaroonCreate runs the generate command -func RunMacaroonCreate(command *cobra.Command, args []string) error { - config, err := command.Flags().GetString("config") - if err != nil { - return err - } - secret := viper.GetString("secret") - if secret == "" { - secret, err = command.Flags().GetString("secret") - if err != nil { - return err - } - } - return Generate( - command.Context(), - config, - secret, - ) -} - -// Generate generates macaroons -func Generate(ctx context.Context, config, secret string) error { - logger, lerr := appctx.GetLogger(ctx) - if lerr != nil { - _, logger = logging.SetupLogger(ctx) - } - - // new config - var tc = new(TokenConfig) - // parse config file - if err := tc.Parse(config); err != nil { - return fmt.Errorf("unable to parse token config: %v", err) - } - - for _, token := range tc.Tokens { - // generate token - t, err := token.Generate(secret) - if err != nil { - logger.Error(). - Err(err). - Msg("unable to generate token") - continue - } - - logger.Info(). - Str("id", token.ID). - Int("secret_length", len(secret)). - Interface("token", t). - Msg("token") - } - return nil -} diff --git a/tools/macaroon/cmd/search_closed_beta.yaml b/tools/macaroon/cmd/search_closed_beta.yaml deleted file mode 100644 index 838ec0db6..000000000 --- a/tools/macaroon/cmd/search_closed_beta.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tokens: - - id: "search closed beta program demo" - version: 2 - location: "search.brave.software" - first_party_caveats: - - sku: "search-beta-access" - - price: 0 - - currency: "BAT" - - description: "Search closed beta program access" - - credential_type: "single-use" diff --git a/tools/macaroon/cmd/token.go b/tools/macaroon/cmd/token.go deleted file mode 100644 index f7d851bf1..000000000 --- a/tools/macaroon/cmd/token.go +++ /dev/null @@ -1,76 +0,0 @@ -package macaroon - -import ( - "encoding/base64" - "fmt" - "os" - - "gopkg.in/macaroon.v2" - "gopkg.in/yaml.v2" -) - -// Caveats - configuration representation of key pair caveats -type Caveats map[string]string - -// Token - configuration representation of token metadata attributes -type Token struct { - ID string `yaml:"id"` - Version int `yaml:"version"` - Location string `yaml:"location"` - FirstPartyCaveats []Caveats `yaml:"first_party_caveats"` -} - -// Generate - Generate a Macaroon from the Token configuration -func (t Token) Generate(secret string) (string, error) { - // create a new macaroon - m, err := macaroon.New([]byte(secret), []byte(t.ID), t.Location, macaroon.Version(t.Version)) - if err != nil { - return "", fmt.Errorf("error creating new macaroon: %w", err) - } - - for _, caveat := range t.FirstPartyCaveats { - for k, v := range caveat { - err := m.AddFirstPartyCaveat( - []byte(fmt.Sprintf("%s=%s", k, v))) - if err != nil { - return "", fmt.Errorf("failed to add caveat: %w", err) - } - } - } - - b, err := m.MarshalBinary() - if err != nil { - return "", fmt.Errorf("error marshal-ing token: %w", err) - } - - return base64.StdEncoding.EncodeToString(b), nil -} - -// TokenConfig - structure of the token configuration file -type TokenConfig struct { - Tokens []Token `yaml:"tokens"` -} - -// Parse - Parse the token configuration file -func (tc *TokenConfig) Parse(path string) (err error) { - // read file - buf, err := os.Open(path) - if err != nil { - err = fmt.Errorf("unable to open file: %w", err) - return - } - defer func() { - // close out file correctly - if err = buf.Close(); err != nil { - err = fmt.Errorf("unable to close file: %w", err) - return - } - }() - - // parse file - if err = yaml.NewDecoder(buf).Decode(tc); err != nil { - err = fmt.Errorf("unable to parse file: %w", err) - return - } - return -} diff --git a/tools/macaroon/cmd/webtest-pj.herokuapp.com.yaml b/tools/macaroon/cmd/webtest-pj.herokuapp.com.yaml deleted file mode 100644 index a03347e5d..000000000 --- a/tools/macaroon/cmd/webtest-pj.herokuapp.com.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tokens: - - id: "webtest-pj.herokuapp.com brave-tshirt v1" - version: 2 - location: "webtest-pj.herokuapp.com" - first_party_caveats: - - sku: "brave-tshirt" - - price: 0.25 - - currency: "BAT" - - description: "" - - credential_type: "single-use" diff --git a/tools/merchant/cmd/README.md b/tools/merchant/cmd/README.md deleted file mode 100644 index f3edb228c..000000000 --- a/tools/merchant/cmd/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Creation of Merchant Attenuated Keys - -In order to create attenuated merchant keys run the command below replacing the encryption-key and -attenuation parameters with valid inputs based on the merchant and environment where these values -will be used. - -```bash -go run main.go merchant create-key --encryption-key lol --attenuation '{"merchant":"brave.com"}' -9:01AM INF encrypted secrets for insertion into database encryptedMerchantNonce=6b297ded477dad5a628d3f2b042eb7a2d415c7e60fa8617f encryptedMerchantSecret=ce16d9539acdd1f5b3767ace17a63032c3b8a4505b22d3cf917b3bdc3db38c329f21bea46fae6dbbb2a601325d4d5ded48f36559ffd619e261a57e4bc6 keyID=ce16d9539acdd1f5b3767ace17a63032c3b8a4505b22d3cf917b3bdc3db38c329f21bea46fae6dbbb2a601325d4d5ded48f36559ffd619e261a57e4bc6 -9:01AM INF attenuated merchant keys aKeyID=5fc33254-a4f8-4424-8186-8d8e1787e00a:eyJtZXJjaGFudCI6ImJyYXZlLmNvbSJ9 aKeySecret=secret-token:wjOtYCQypY5ky1AM_co1lTXNJdOe3Q_waNnnfdyl5u3eOKHCKL-galY9Wklf merchantSecret=secret-token:a0Ojj4_TBtX_aXSowKByJ-NVND9kf2t4 -``` - -In the result-set you will see the attenuated merchant `aKeyID` and `aKeySecret` which need to be securely -delivered to the merchant. You will also see the `encryptedMerchantSecret` and `encryptedMerchantNonce` and -`keyID` which need to be inserted into the environment's database `api_keys` table. - - diff --git a/tools/merchant/cmd/new_keys.go b/tools/merchant/cmd/new_keys.go deleted file mode 100644 index 467a62d7a..000000000 --- a/tools/merchant/cmd/new_keys.go +++ /dev/null @@ -1,154 +0,0 @@ -package merchant - -import ( - "context" - "crypto/rand" - "encoding/base64" - "encoding/json" - "fmt" - - "github.com/brave-intl/bat-go/cmd" - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/google/uuid" - "github.com/spf13/cobra" -) - -var ( - // MerchantCmd is a subcommand for macaroons - MerchantCmd = &cobra.Command{ - Use: "merchant", - Short: "merchant subcommand", - } - // MerchantCreateKeyCmd generates a merchant attenuated key and id - MerchantCreateKeyCmd = &cobra.Command{ - Use: "create-key", - Short: "create a merchant key", - Run: cmd.Perform("merchant key creation", RunMerchantCreateKey), - } -) - -func init() { - MerchantCmd.AddCommand( - MerchantCreateKeyCmd, - ) - cmd.RootCmd.AddCommand( - MerchantCmd, - ) - - createBuilder := cmdutils.NewFlagBuilder(MerchantCreateKeyCmd) - - createBuilder.Flag().String("encryption-key", "", - "the environment encryption key"). - Bind("encryption-key"). - Env("ENCRYPTION_KEY") - - createBuilder.Flag().String("attenuation", "{}", - "the attenuation for the created key"). - Bind("attenuation") -} - -// RunMerchantCreateKey runs the generate command -func RunMerchantCreateKey(command *cobra.Command, args []string) error { - encryptionKey, err := command.Flags().GetString("encryption-key") - if err != nil { - return err - } - if encryptionKey == "" { - panic("no environment encryption key") - } - - attenuation, err := command.Flags().GetString("attenuation") - if err != nil { - return err - } - if attenuation == "" { - panic("no attenuation for key") - } - return CreateKey( - command.Context(), - encryptionKey, - attenuation, - ) -} - -// CreateKey - creates an attenuated merchant key -func CreateKey(ctx context.Context, encryptionKey, attenuation string) error { - logger, lerr := appctx.GetLogger(ctx) - if lerr != nil { - _, logger = logging.SetupLogger(ctx) - } - - var ( - merchantSecret string - keyID string = uuid.New().String() - aKeyID string - aKeySecret string - err error - ) - - // generate shared merchant secret - merchantSecret, err = randomSecret(24) - if err != nil { - logger.Panic(). - Err(err). - Msg("failed to generate merchant keys") - return nil - } - // encrypt shared merchant secret - var byteEncryptionKey [32]byte - copy(byteEncryptionKey[:], []byte(encryptionKey)) // use env encrypt key - - encryptedBytes, n, err := cryptography.EncryptMessage(byteEncryptionKey, []byte(merchantSecret)) - if err != nil { - logger.Panic(). - Err(err). - Msg("failed to encrypt merchant keys") - return nil - } - - logger.Info(). - Str("keyID", fmt.Sprintf("%s", keyID)). - Str("encryptedMerchantSecret", fmt.Sprintf("%x", encryptedBytes)). - Str("encryptedMerchantNonce", fmt.Sprintf("%x", n)). - Msg("encrypted secrets for insertion into database") - - // get the attenuation caveats - var caveats = map[string]string{} - err = json.Unmarshal([]byte(attenuation), &caveats) - if err != nil { - logger.Panic(). - Err(err). - Msg("failed to decode attenuation caveats") - return nil - } - - aKeyID, aKeySecret, err = cryptography.Attenuate(keyID, merchantSecret, caveats) - if err != nil { - logger.Panic(). - Err(err). - Msg("failed to attenuate merchant key") - return nil - } - - logger.Info(). - Str("merchantSecret", merchantSecret). - Str("aKeyID", aKeyID). - Str("aKeySecret", aKeySecret). - Msg("attenuated merchant keys") - - return nil -} - -func randomSecret(n int) (string, error) { - b := make([]byte, n) - _, err := rand.Read(b) - // Note that err == nil only if we read len(b) bytes. - if err != nil { - return "", err - } - - return "secret-token:" + base64.RawURLEncoding.EncodeToString(b), nil -} diff --git a/tools/payments/cmd/authorize/go.mod b/tools/payments/cmd/authorize/go.mod deleted file mode 100644 index a5ae981e5..000000000 --- a/tools/payments/cmd/authorize/go.mod +++ /dev/null @@ -1,52 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/authorize - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 - github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 -) - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) diff --git a/tools/payments/cmd/authorize/go.sum b/tools/payments/cmd/authorize/go.sum deleted file mode 100644 index fdc5c8bd3..000000000 --- a/tools/payments/cmd/authorize/go.sum +++ /dev/null @@ -1,135 +0,0 @@ -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tools/payments/cmd/authorize/main.go b/tools/payments/cmd/authorize/main.go index 30571bb46..b7b215478 100644 --- a/tools/payments/cmd/authorize/main.go +++ b/tools/payments/cmd/authorize/main.go @@ -36,7 +36,7 @@ import ( "strings" "github.com/brave-intl/bat-go/libs/payments" - paymentscli "github.com/brave-intl/bat-go/tools/payments" + paymentscli "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/bootstrap/go.mod b/tools/payments/cmd/bootstrap/go.mod deleted file mode 100644 index 92d44c88c..000000000 --- a/tools/payments/cmd/bootstrap/go.mod +++ /dev/null @@ -1,73 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/bootstrap - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - filippo.io/age v1.1.1 - github.com/aws/aws-sdk-go-v2 v1.18.0 - github.com/aws/aws-sdk-go-v2/config v1.18.19 - github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 - github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1 - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 - github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 -) - -require ( - filippo.io/edwards25519 v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.18 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 // indirect - github.com/aws/smithy-go v1.13.5 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) diff --git a/tools/payments/cmd/bootstrap/go.sum b/tools/payments/cmd/bootstrap/go.sum deleted file mode 100644 index 5b438e3a3..000000000 --- a/tools/payments/cmd/bootstrap/go.sum +++ /dev/null @@ -1,188 +0,0 @@ -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= -github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw= -github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY= -github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c= -github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU= -github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1 h1:PJH4I+qYjPXclKRbVCW47iYUvtXEh1u6YmDhn5J8VQE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tools/payments/cmd/bootstrap/main.go b/tools/payments/cmd/bootstrap/main.go index 5f83bfa12..ccd6cb045 100644 --- a/tools/payments/cmd/bootstrap/main.go +++ b/tools/payments/cmd/bootstrap/main.go @@ -44,7 +44,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" paymentslib "github.com/brave-intl/bat-go/libs/payments" - "github.com/brave-intl/bat-go/tools/payments" + "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/chain-account/go.mod b/tools/payments/cmd/chain-account/go.mod deleted file mode 100644 index 8364c85a7..000000000 --- a/tools/payments/cmd/chain-account/go.mod +++ /dev/null @@ -1,52 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/chain-account - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 - github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 -) - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) diff --git a/tools/payments/cmd/chain-account/go.sum b/tools/payments/cmd/chain-account/go.sum deleted file mode 100644 index fdc5c8bd3..000000000 --- a/tools/payments/cmd/chain-account/go.sum +++ /dev/null @@ -1,135 +0,0 @@ -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tools/payments/cmd/chain-account/main.go b/tools/payments/cmd/chain-account/main.go index d043acca8..1cc12455f 100644 --- a/tools/payments/cmd/chain-account/main.go +++ b/tools/payments/cmd/chain-account/main.go @@ -35,7 +35,7 @@ import ( client "github.com/brave-intl/bat-go/libs/clients" "github.com/brave-intl/bat-go/libs/httpsignature" "github.com/brave-intl/bat-go/libs/payments" - paymentscli "github.com/brave-intl/bat-go/tools/payments" + paymentscli "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/configure/go.mod b/tools/payments/cmd/configure/go.mod deleted file mode 100644 index b4a43344d..000000000 --- a/tools/payments/cmd/configure/go.mod +++ /dev/null @@ -1,36 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/configure - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - filippo.io/age v1.1.1 - github.com/aws/aws-sdk-go v1.45.21 - github.com/aws/aws-sdk-go-v2/config v1.18.43 - github.com/aws/aws-sdk-go-v2/service/kms v1.24.5 - github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 -) - -require ( - github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.41 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.15.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.23.0 // indirect - github.com/aws/smithy-go v1.14.2 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.6.0 // indirect -) diff --git a/tools/payments/cmd/configure/go.sum b/tools/payments/cmd/configure/go.sum deleted file mode 100644 index 33d2cf074..000000000 --- a/tools/payments/cmd/configure/go.sum +++ /dev/null @@ -1,84 +0,0 @@ -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -github.com/aws/aws-sdk-go v1.45.21 h1:9LN2/pMQY2qwGJgSMzv0xNGnstFHCgpSfenvsurX2nk= -github.com/aws/aws-sdk-go v1.45.21/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= -github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= -github.com/aws/aws-sdk-go-v2/config v1.18.43 h1:IgdUtTRvUDC6eiJBqU6vh7bHFNAEBjQ8S+qJ7zVhDOs= -github.com/aws/aws-sdk-go-v2/config v1.18.43/go.mod h1:NiFev8qlgg8MPzw3fO/EwzMZeZwlJEKGwfpjRPA9Nvw= -github.com/aws/aws-sdk-go-v2/credentials v1.13.41 h1:dgbKq1tamtboYAKSXWbqL0lKO9rmEzEhbZFh9JQW/Bg= -github.com/aws/aws-sdk-go-v2/credentials v1.13.41/go.mod h1:cc3Fn7DkKbJalPtQnudHGZZ8ml9+hwtbc1CJONsYYqk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 h1:g+qlObJH4Kn4n21g69DjspU0hKTjWtq7naZ9OLCv0ew= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8= -github.com/aws/aws-sdk-go-v2/service/kms v1.24.5 h1:VNEw+EdYDUdkICYAVQ6n9WoAq8ZuZr7dXKjyaOw94/Q= -github.com/aws/aws-sdk-go-v2/service/kms v1.24.5/go.mod h1:NZEhPgq+vvmM6L9w+xl78Vf7YxqUcpVULqFdrUhHg8I= -github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0 h1:wl5dxN1NONhTDQD9uaEvNsDRX29cBmGED/nl0jkWlt4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.40.0/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.0 h1:vuGK1vHNP9zx0PfOrtPumbwR2af0ATQ1Z2H6p75AgRQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.0/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 h1:8lKOidPkmSmfUtiTgtdXWgaKItCZ/g75/jEk6Ql6GsA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.0 h1:pyvfUqkNLMipdKNAtu7OVbRxUrR2BMaKccIPpk/Hkak= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= -github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= -github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/tools/payments/cmd/disaster/go.sum b/tools/payments/cmd/disaster/go.sum deleted file mode 100644 index 7a812d0b8..000000000 --- a/tools/payments/cmd/disaster/go.sum +++ /dev/null @@ -1,192 +0,0 @@ -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/alicebob/miniredis/v2 v2.23.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.50.13 h1:yeXram2g7q8uKkQkAEeZyk9FmPzxI4UpGwAZGZtEGmM= -github.com/aws/aws-sdk-go v1.50.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= -github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg= -github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA= -github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE= -github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs= -github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 h1:81KE7vaZzrl7yHBYHVEzYB8sypz11NMOZ40YlWvPxsU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5/go.mod h1:LIt2rg7Mcgn09Ygbdh/RdIm0rQ+3BNkbP1gyVMFtRK0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 h1:ZMeFZ5yk+Ek+jNr1+uwCd2tG89t6oTS5yVWpa6yy2es= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7/go.mod h1:mxV05U+4JiHqIpGqqYXOHLPKUC6bDXC44bsUhNjOEwY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 h1:f9RyWNtS8oH7cZlbn+/JNPpjUk5+5fLd5lM9M0i49Ys= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5/go.mod h1:h5CoMZV2VF297/VLhRhO1WF+XYWOzXo+4HsObA4HjBQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 h1:6cnno47Me9bRykw9AEv9zkXE+5or7jz8TsskTTccbgc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1/go.mod h1:qmdkIIAC+GCLASF7R2whgNrJADz0QZPX+Seiw/i4S3o= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= -github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= -github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= -github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= -github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/vault v1.16.2 h1:8usz1tMhWcl18PbdjQPKoL5UmenA2n4ZKB3oM04jTck= -github.com/hashicorp/vault v1.16.2/go.mod h1:QqhZjf0dS0krRty6Wf8iSMx2EnIjAJSCpZ57jEmkQec= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/payments/cmd/disaster/main.go b/tools/payments/cmd/disaster/main.go index e5cacdb14..e0d39648b 100644 --- a/tools/payments/cmd/disaster/main.go +++ b/tools/payments/cmd/disaster/main.go @@ -45,7 +45,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go/aws" "github.com/brave-intl/bat-go/libs/requestutils" - "github.com/brave-intl/bat-go/tools/payments" + "github.com/brave-intl/payments-service/tools/payments" "github.com/hashicorp/vault/shamir" ) diff --git a/tools/payments/cmd/prepare/go.mod b/tools/payments/cmd/prepare/go.mod deleted file mode 100644 index a5259e423..000000000 --- a/tools/payments/cmd/prepare/go.mod +++ /dev/null @@ -1,52 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/prepare - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 - github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 -) - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) diff --git a/tools/payments/cmd/prepare/go.sum b/tools/payments/cmd/prepare/go.sum deleted file mode 100644 index fdc5c8bd3..000000000 --- a/tools/payments/cmd/prepare/go.sum +++ /dev/null @@ -1,135 +0,0 @@ -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tools/payments/cmd/prepare/main.go b/tools/payments/cmd/prepare/main.go index 676e226a9..fbd1ad53d 100644 --- a/tools/payments/cmd/prepare/main.go +++ b/tools/payments/cmd/prepare/main.go @@ -31,7 +31,7 @@ import ( "strings" "github.com/brave-intl/bat-go/libs/payments" - paymentscli "github.com/brave-intl/bat-go/tools/payments" + paymentscli "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/report/go.mod b/tools/payments/cmd/report/go.mod deleted file mode 100644 index 0c0b08b50..000000000 --- a/tools/payments/cmd/report/go.mod +++ /dev/null @@ -1,52 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/report - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 - github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 -) - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) diff --git a/tools/payments/cmd/report/go.sum b/tools/payments/cmd/report/go.sum deleted file mode 100644 index fdc5c8bd3..000000000 --- a/tools/payments/cmd/report/go.sum +++ /dev/null @@ -1,135 +0,0 @@ -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tools/payments/cmd/report/main.go b/tools/payments/cmd/report/main.go index 27cb2edd3..b99bf130c 100644 --- a/tools/payments/cmd/report/main.go +++ b/tools/payments/cmd/report/main.go @@ -28,7 +28,7 @@ import ( "strings" "github.com/brave-intl/bat-go/libs/payments" - paymentscli "github.com/brave-intl/bat-go/tools/payments" + paymentscli "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/status/go.mod b/tools/payments/cmd/status/go.mod deleted file mode 100644 index ee6ccfe45..000000000 --- a/tools/payments/cmd/status/go.mod +++ /dev/null @@ -1,50 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/status - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require github.com/brave-intl/bat-go/tools/payments v0.0.0-20231012115000-3de8c63b05ca - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.28.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect -) diff --git a/tools/payments/cmd/status/go.sum b/tools/payments/cmd/status/go.sum deleted file mode 100644 index 04ffd7d38..000000000 --- a/tools/payments/cmd/status/go.sum +++ /dev/null @@ -1,565 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/tools/payments/cmd/status/main.go b/tools/payments/cmd/status/main.go index 8d7985629..a72dff776 100644 --- a/tools/payments/cmd/status/main.go +++ b/tools/payments/cmd/status/main.go @@ -30,7 +30,7 @@ import ( "log" "strings" - paymentscli "github.com/brave-intl/bat-go/tools/payments" + paymentscli "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/validate/go.mod b/tools/payments/cmd/validate/go.mod deleted file mode 100644 index bd763f543..000000000 --- a/tools/payments/cmd/validate/go.mod +++ /dev/null @@ -1,50 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/validate - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/brave-intl/bat-go/libs v0.0.0-20230403164253-ad445ff37512 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) diff --git a/tools/payments/cmd/validate/go.sum b/tools/payments/cmd/validate/go.sum deleted file mode 100644 index fdc5c8bd3..000000000 --- a/tools/payments/cmd/validate/go.sum +++ /dev/null @@ -1,135 +0,0 @@ -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tools/payments/cmd/validate/main.go b/tools/payments/cmd/validate/main.go index 0f234d342..1d44eabf9 100644 --- a/tools/payments/cmd/validate/main.go +++ b/tools/payments/cmd/validate/main.go @@ -24,7 +24,7 @@ import ( "log" "os" - "github.com/brave-intl/bat-go/tools/payments" + "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/cmd/vault/go.mod b/tools/payments/cmd/vault/go.mod deleted file mode 100644 index 276658172..000000000 --- a/tools/payments/cmd/vault/go.mod +++ /dev/null @@ -1,57 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments/cmd/vault - -replace github.com/brave-intl/bat-go/tools/payments => ../../ - -replace github.com/brave-intl/bat-go/libs => ../../../../libs - -go 1.20 - -require ( - github.com/brave-intl/bat-go/libs v0.0.0-00010101000000-000000000000 - github.com/brave-intl/bat-go/tools/payments v0.0.0-00010101000000-000000000000 -) - -require ( - filippo.io/age v1.1.1 // indirect - filippo.io/edwards25519 v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/getsentry/sentry-go v0.14.0 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.28.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shengdoushi/base58 v1.0.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/throttled/throttled v2.2.5+incompatible // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.29.1 // indirect -) diff --git a/tools/payments/cmd/vault/go.sum b/tools/payments/cmd/vault/go.sum deleted file mode 100644 index 86f983127..000000000 --- a/tools/payments/cmd/vault/go.sum +++ /dev/null @@ -1,577 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70= -github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= -github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= -github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/tools/payments/cmd/vault/main.go b/tools/payments/cmd/vault/main.go index 2cfe362f9..1ec119f8b 100644 --- a/tools/payments/cmd/vault/main.go +++ b/tools/payments/cmd/vault/main.go @@ -44,7 +44,7 @@ import ( "filippo.io/age/agessh" client "github.com/brave-intl/bat-go/libs/clients" "github.com/brave-intl/bat-go/libs/payments" - paymentscli "github.com/brave-intl/bat-go/tools/payments" + paymentscli "github.com/brave-intl/payments-service/tools/payments" ) func main() { diff --git a/tools/payments/go.mod b/tools/payments/go.mod deleted file mode 100644 index ca8f2e9a3..000000000 --- a/tools/payments/go.mod +++ /dev/null @@ -1,49 +0,0 @@ -module github.com/brave-intl/bat-go/tools/payments - -go 1.19 - -replace github.com/brave-intl/bat-go/libs => ../../libs - -require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d - github.com/brave-intl/bat-go/libs v0.0.0-00010101000000-000000000000 - github.com/google/uuid v1.3.0 - github.com/shopspring/decimal v1.3.1 - github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.14.0 -) - -require ( - filippo.io/age v1.1.1 // indirect - filippo.io/edwards25519 v1.0.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 // indirect - github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/mdlayher/vsock v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/redis/go-redis/v9 v9.3.0 // indirect - github.com/rs/zerolog v1.28.0 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/tools/payments/go.sum b/tools/payments/go.sum deleted file mode 100644 index 785214e53..000000000 --- a/tools/payments/go.sum +++ /dev/null @@ -1,554 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703 h1:oTi0zYvHo1sfk5sevGc4LrfgpLYB6cIhP/HllCUGcZ8= -github.com/hf/nitrite v0.0.0-20211104000856-f9e0dcc73703/go.mod h1:ycRhVmo6wegyEl6WN+zXOHUTJvB0J2tiuH88q/McTK8= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9 h1:pU32bJGmZwF4WXb9Yaz0T8vHDtIPVxqDOdmYdwTQPqw= -github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9/go.mod h1:MJsac5D0fKcNWfriUERtln6segcGfD6Nu0V5uGBbPf8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mdlayher/vsock v1.2.0 h1:klRY9lndjmg6k/QWbX/ucQ3e2JFRm1M7vfG9hijbQ0A= -github.com/mdlayher/vsock v1.2.0/go.mod h1:w4kdSTQB9p1l/WwGmAs0V62qQ869qRYoongwgN+Y1HE= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/tools/payments/report_test.go b/tools/payments/report_test.go index 2d655cac0..44b818f2b 100644 --- a/tools/payments/report_test.go +++ b/tools/payments/report_test.go @@ -8,7 +8,7 @@ import ( should "github.com/stretchr/testify/assert" paymentLib "github.com/brave-intl/bat-go/libs/payments" - "github.com/brave-intl/bat-go/tools/payments" + "github.com/brave-intl/payments-service/tools/payments" ) func TestAttestedReport_EnsureUniqueDest(t *testing.T) { diff --git a/tools/payments/share-test-operator-age1eqa6hw3zjvazdvflh4gpp4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc b/tools/payments/share-test-operator-age1eqa6hw3zjvazdvflh4gpp4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc new file mode 100644 index 0000000000000000000000000000000000000000..6e7319b9be9fb7f68b5627690c06f0039925046b GIT binary patch literal 312 zcmV-80muGfXJsvAZewzJaCB*JZZ2>_#MQLS9R#j0?bwN>4T5@!0L_rE@T5&TqdO>nQ zVryzaLu5%rRBUWQH&1a)R!ezyPi<>#Y-w0DRyA`uNoNWzEiE89c}Yw#LNH2tRBCE3 zdTM%jNKh~_dSfGSFGirFEO=jatxF4@UahlXpNJ?;-HW@t^63Dy=ILB}k+_$iWE7`cIH3<0?3m5y zt*3^jw-X)WylJOlFUZx+p=zl3?6-bi=n2-&DtRP=BD_!m{rSb1=MFq(FPR#uDn059 KoVimTQ{z%17JIk= literal 0 HcmV?d00001 diff --git a/tools/payments/share-test-operator2-age1eqa6hw3zjvazdvflh4gpp4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc b/tools/payments/share-test-operator2-age1eqa6hw3zjvazdvflh4gpp4cs46gpsr9r6n7c3j4qtzh875qpzg6sh3ev88.enc new file mode 100644 index 0000000000000000000000000000000000000000..133a9bcfdcf0f57ccf416bd3e6014fb8a802e90f GIT binary patch literal 312 zcmV-80muGfXJsvAZewzJaCB*JZZ2T6=GGXf`)^D@9ONdS?nPEiE8rGfsLzD{)h1L})>0 zGG$>{Wn@x!cX)DfPexWTXIWu3aAHq5cXmT}bV&+8jWC4(W7> ~/.profile - -# set in local shell -export VAULT_ADDR=http://127.0.0.1:8200 - -# start vault server -./vault server -config=config.hcl - -# initialize -./bat-go vault init --key-shares 1 --key-threshold 1 PATH_TO_GPG_PUB_KEY_FILE - -# unseal vault -gpg -d ./share-0.gpg | ./bat-go vault unseal -# -> prompt for password -``` - -## Bringing up vault - -On the offline computer, in one window run: -``` -./vault server -config=config.hcl -``` - -In another run: -``` -gpg -d ./share-0.gpg | ./bat-go vault unseal -``` - -## Creating a vault config -The `config.example.yaml` should be copied wherever it is easiest to point to. Just pass in the path while running a command that interacts with vault (import-key, sign-settlement) etc. Be sure to change the values of the wallets to suit your setup if required. - -The values name should be unique, as we use the label online (e.g. creating a wallet with the label name). - -As of May 2021, the following keys are accepted: - -```bash -wallets: - uphold-contribution: 'prod-wallet-contributions-YYYY-MM' - uphold-referral: 'prod-wallet-referrals-YYYY-MM' - gemini-contribution: 'prod-wallet-contributions-YYYY-MM' - gemini-referral: 'prod-wallet-referrals-YYYY-MM' -``` - -## Importing keys - -the following line imports the keys from the following environment variables: -```bash -ED25519_PRIVATE_KEY= \ -ED25519_PUBLIC_KEY= \ -UPHOLD_PROVIDER_ID= \ -GEMINI_CLIENT_ID= \ -GEMINI_CLIENT_KEY= \ -GEMINI_CLIENT_SECRET= \ -./bat-go vault import-key --config=./config.yaml -# pass a known key to only import one: --wallet-refs=gemini-referral -``` - -## Running settlement - -First bring up vault as described above. - -``` -./bat-go vault sign-settlement --config=./config.yaml --in=SETTLEMENT_REPORT.JSON -``` - -Finally seal the vault: -``` -./vault operator seal -``` -You can now stop the server instance. - -Copy the signed json flie to the usb stick and transfer it to the online -computer. - -On the online computer: -``` -export UPHOLD_ENVIRONMENT= -export UPHOLD_HTTP_PROXY= -export UPHOLD_ACCESS_TOKEN= -export UPHOLD_SETTLEMENT_ADDRESS= -export VAULT_ADDR= -./settlement-submit -in -``` - -Note that you can run submit multiple times, progress is tracked in a log to -allow restoring from errors and to avoid duplicate payouts. - -Finally upload the "-finished" output file to eyeshade to account for payout -transactions that were made. - -## Creating a new offline wallet - -On the offline machine, first bring up vault as described above. - -Run vault-create-wallet, this will sign the registration and store it into -a local file: -``` -./bat-go vault create-wallet NAME_OF_NEW_WALLET -``` - -Copy the created `name-of-new-wallet-registration.json` file to the online -machine. - -Re-run vault-create-wallet, this will submit the pre-signed registration: -### Uphold -``` -export UPHOLD_ENVIRONMENT= -export UPHOLD_HTTP_PROXY= -export UPHOLD_ACCESS_TOKEN= -export VAULT_ADDR= -./bat-go vault create-wallet -offline NAME_OF_NEW_WALLET -``` - -### Gemini -``` -export GEMINI_CLIENT_ID -export GEMINI_CLIENT_KEY -export GEMINI_CLIENT_SECRET -export GEMINI_SERVER -export GEMINI_SUBMIT_TYPE -export VAULT_ADDR -``` - -Finally copy `name-of-new-wallet-registration.json` back to the offline -machine and run vault-create-wallet to record the provider ID in vault: -``` -./bat-go vault create-wallet -offline NAME_OF_NEW_WALLET -``` - -## Signing Files -Signing the settlement file will split the input files into many output files depending on the contents of the file - -### Uphold -```bash -./bat-go vault sign-settlement --config=./config.yaml --in=contributions.json -``` - -### Gemini -```bash -./bat-go vault sign-settlement --config=publishers-gemini.yaml --in=publishers-payout-report-gemini-referrals.json --providers=gemini -``` - -## Uploading files - -Running `settlement-submit` with a provider tells the script where to submit the file and the kind of handler to use. - -### Uphold -```bash -./settlement-submit -in=gemini-contributions-signed.json -provider=uphold -``` - -### Gemini - -gemini has a command available to it for uploading transactions and sending -```bash -./bat-go settlement gemini upload --input=gemini-referral-publishers-payout-report-gemini-referrals-signed.json --all-txs-input=publishers-payout-report-gemini-referrals.json -``` - -and to check the status of each transaction a `checkstatus` command has been added -```bash -./bat-go settlement gemini checkstatus --input=bulk-signed-transactions.json --all-txs-input=from-antifraud.json -``` diff --git a/tools/settlement/bitflyer/upload.go b/tools/settlement/bitflyer/upload.go deleted file mode 100644 index 50cb039a5..000000000 --- a/tools/settlement/bitflyer/upload.go +++ /dev/null @@ -1,464 +0,0 @@ -package bitflyersettlement - -import ( - "context" - "errors" - "fmt" - "math" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients/bitflyer" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/tools/settlement" - "github.com/shopspring/decimal" - "github.com/spf13/viper" -) - -var ( - notSubmittedCategory = "not-submitted" -) - -// GroupSettlements groups settlements under a single wallet provider id so that we can impose limits based on price -// no signing here, just grouping settlements under a single deposit id -func GroupSettlements( - settlements *[]custodian.Transaction, -) map[string][]custodian.Transaction { - groupedByWalletProviderID := make(map[string][]custodian.Transaction) - - for _, payout := range *settlements { - if payout.WalletProvider == "bitflyer" { - walletProviderID := payout.WalletProviderID - if groupedByWalletProviderID[walletProviderID] == nil { - groupedByWalletProviderID[walletProviderID] = []custodian.Transaction{} - } - groupedByWalletProviderID[walletProviderID] = append(groupedByWalletProviderID[walletProviderID], payout) - } - } - return groupedByWalletProviderID -} - -// CategorizeResponse categorizes a response from bitflyer as pending, complete, failed, or unknown -func CategorizeResponse( - batchByTransferID map[string]settlement.AggregateTransaction, - payout *bitflyer.WithdrawToDepositIDResponse, -) ([]custodian.Transaction, string) { - currentTx := batchByTransferID[payout.TransferID] - key := payout.CategorizeStatus() - - currentTx.Status = key - note := "" - if payout.Message != "" { - note = fmt.Sprintf("%s: %s. transferID: %s", payout.Status, payout.Message, payout.TransferID) - } else { - note = fmt.Sprintf("%s transferID: %s", payout.Status, payout.TransferID) - } - - for i, tx := range currentTx.Inputs { - // overwrite the amount - tx.Note = note - tmp := altcurrency.BAT - tx.AltCurrency = &tmp - tx.Currency = tmp.String() - tx.Status = key - currentTx.Inputs[i] = tx - } - return currentTx.Inputs, key -} - -// CategorizeResponses categorizes the series of responses -func CategorizeResponses( - batch []settlement.AggregateTransaction, - response *[]bitflyer.WithdrawToDepositIDResponse, -) map[string][]custodian.Transaction { - transactions := make(map[string][]custodian.Transaction) - batchByTransferID := make(map[string]settlement.AggregateTransaction) - - for _, tx := range batch { - batchByTransferID[tx.BitflyerTransferID()] = tx - } - - for _, payout := range *response { - inputs, key := CategorizeResponse( - batchByTransferID, - &payout, - ) - transactions[key] = append(transactions[key], inputs...) - } - return transactions -} - -// SubmitBulkPayoutTransactions submits bulk payout transactions -func SubmitBulkPayoutTransactions( - ctx context.Context, - batch []settlement.AggregateTransaction, - submittedTransactions map[string][]custodian.Transaction, - bulkPayoutRequestRequirements bitflyer.WithdrawToDepositIDBulkPayload, - bitflyerClient bitflyer.Client, - total int, - blockProgress int, -) (map[string][]custodian.Transaction, error) { - logger := logging.FromContext(ctx) - logging.SubmitProgress(ctx, blockProgress, total) - - logger.Debug(). - Int("total", total). - Int("progress", blockProgress). - Msg("sending request") - - response, err := bitflyerClient.UploadBulkPayout( - ctx, - bulkPayoutRequestRequirements, - ) - <-time.After(time.Second) - if err != nil { - logger.Error().Err(err).Msg("error performing upload") - return submittedTransactions, err - } - // collect all successful transactions to send to eyeshade - submitted := CategorizeResponses( - batch, - &response.Withdrawals, - ) - for key, txs := range submitted { - submittedTransactions[key] = append(submittedTransactions[key], txs...) - } - return submittedTransactions, nil -} - -// CheckPayoutTransactionsStatus checks the status of given transactions -func CheckPayoutTransactionsStatus( - ctx context.Context, - batch []settlement.AggregateTransaction, - submittedTransactions map[string][]custodian.Transaction, - bulkPayoutRequestRequirements bitflyer.WithdrawToDepositIDBulkPayload, - bitflyerClient bitflyer.Client, - total int, - blockProgress int, -) (map[string][]custodian.Transaction, error) { - logger := logging.FromContext(ctx) - - result, err := bitflyerClient.CheckPayoutStatus( - ctx, - bulkPayoutRequestRequirements.ToBulkStatus(), - ) - if err != nil { - return nil, err - } - response := CategorizeResponses( - batch, - &result.Withdrawals, - ) - for key, original := range response { - submittedTransactions[key] = append(submittedTransactions[key], original...) - prog := blockProgress - logging.SubmitProgress(ctx, prog, total) - logger.Debug(). - Int("total", total). - Int("progress", prog). - Str("key", key). - Str("tx_ref", original[0].DocumentID). - Msg("parameters used") - } - return submittedTransactions, err -} - -func setupSettlementTransactions( - transactionsByProviderID map[string][]custodian.Transaction, - probiLimit decimal.Decimal, - excludeLimited bool, - sourceFrom string, -) ( - []settlement.AggregateTransaction, - []custodian.Transaction, - int, - error, -) { - // goes to bitflyer, does not include 0 value txs - settlements := []settlement.AggregateTransaction{} - // a list of settlements that are not being sent - notSubmittedSettlements := []custodian.Transaction{} - // number of transactions whose amounts were reduced - numReduced := 0 - - for _, groupedWithdrawals := range transactionsByProviderID { - aggregatedTx := settlement.AggregateTransaction{} - providerIDProbiLimit := probiLimit - for groupedWithdrawalIndex, limitedTx := range groupedWithdrawals { - if groupedWithdrawalIndex == 0 { - aggregatedTx.AltCurrency = limitedTx.AltCurrency - aggregatedTx.Currency = limitedTx.Currency - aggregatedTx.Destination = limitedTx.Destination - aggregatedTx.Publisher = limitedTx.Publisher - aggregatedTx.WalletProvider = limitedTx.WalletProvider - aggregatedTx.WalletProviderID = limitedTx.WalletProviderID - aggregatedTx.ProviderID = limitedTx.WalletProviderID - aggregatedTx.Channel = limitedTx.Channel - aggregatedTx.SettlementID = limitedTx.SettlementID - aggregatedTx.Type = limitedTx.Type - aggregatedTx.Inputs = []custodian.Transaction{} - } - partialProbi := limitedTx.Probi - // will hit our limits - if aggregatedTx.Probi.Add(partialProbi).GreaterThan(providerIDProbiLimit) { - // reduce amount and fee to be within. can be zero - if excludeLimited { - // if we are excluding any limited transactions, - // then simply reduce the limit for that bitflyer wallet - providerIDProbiLimit = aggregatedTx.Probi - } else { - numReduced++ - } - partialProbi = providerIDProbiLimit.Sub(aggregatedTx.Probi) - } - partialFee := decimal.Zero - if limitedTx.BATPlatformFee.GreaterThan(decimal.Zero) { - partialFee = partialProbi.Div(decimal.NewFromFloat(19)).Truncate(0) - } - // always in BAT to BAT so we're good - partialAmount := altcurrency.BAT.FromProbi(partialProbi) - // add to aggregate provider transaction - aggregatedTx.Amount = aggregatedTx.Amount.Add(partialAmount) - // not needed but useful for sanity checking - aggregatedTx.BATPlatformFee = aggregatedTx.BATPlatformFee.Add(partialFee) - aggregatedTx.Probi = aggregatedTx.Probi.Add(partialProbi) - // attach to upper levels - if partialProbi.Equals(decimal.Zero) { - limitedTx.Status = "not-submitted" - limitedTx.Note = "MONTHLY_SEND_LIMIT: not-submitted" - notSubmittedSettlements = append(notSubmittedSettlements, limitedTx) - } else { - aggregatedTx.Inputs = append(aggregatedTx.Inputs, limitedTx) - // need separate so we can settle different types on eyeshade - // update single settlement. - limitedTx.Amount = partialAmount - limitedTx.BATPlatformFee = partialFee - limitedTx.Probi = partialProbi - } - } - if !aggregatedTx.Probi.Equals(decimal.Zero) { - //Bitflyer Specific requirement to truncate into 8 places or we will get API errors - aggregatedTx.Probi = altcurrency.BAT.ToProbi(altcurrency.BAT.FromProbi(aggregatedTx.Probi).Truncate(8)) - aggregatedTx.SourceFrom = sourceFrom - settlements = append(settlements, aggregatedTx) - } - } - return settlements, notSubmittedSettlements, numReduced, nil -} - -func CreateBitflyerRequest( - dryRun *bitflyer.DryRunOption, - token string, - settlementRequests []settlement.AggregateTransaction, -) (*bitflyer.WithdrawToDepositIDBulkPayload, error) { - set := []custodian.Transaction{} - sourceFrom := "" - for _, tx := range settlementRequests { - set = append(set, tx.Transaction) - sourceFrom = tx.SourceFrom - } - bitflyerPayloads, err := bitflyer.NewWithdrawsFromTxs( - sourceFrom, - set, - ) - if err != nil { - return nil, err - } - bitflyerRequest := bitflyer.NewWithdrawToDepositIDBulkPayload( - dryRun, - token, - bitflyerPayloads, - ) - return bitflyerRequest, nil -} - -// PrepareRequests prepares requests -func PrepareRequests( - ctx context.Context, - bitflyerClient bitflyer.Client, - txs []custodian.Transaction, - excludeLimited bool, - sourceFrom string, -) (*PreparedTransactions, error) { - logger := logging.FromContext(ctx) - - quote, err := bitflyerClient.FetchQuote(ctx, "BAT_JPY", true) - if err != nil { - return nil, err - } - - rate := quote.Rate - - // group by wallet provider id - groupedByWalletProviderID := GroupSettlements(&txs) - - logger.Info().Int("count", len(groupedByWalletProviderID)).Msg("grouped bf transactions") - - // bat limit - probiLimit := altcurrency.BAT.ToProbi(decimal.NewFromFloat32(200000). // start with jpy - Div(rate). // convert to bat - Mul(decimal.NewFromFloat(0.9)). // reduce by an extra 10% if we're paranoid - Truncate(8)) // truncated to satoshis - transactions, notSubmittedTransactions, numReduced, err := setupSettlementTransactions( - groupedByWalletProviderID, - probiLimit, - excludeLimited, - sourceFrom, - ) - if err != nil { - return nil, err - } - - jpyBATRate, _ := rate.Float64() - - transactionBatches := batchTransactions(ctx, transactions) - - logger.Info().Float64("JPY/BAT", jpyBATRate).Int("batches", len(transactionBatches)).Int("ignored", len(notSubmittedTransactions)).Int("reduced", numReduced).Msg("prepared bf transactions") - - return &PreparedTransactions{ - AggregateTransactionBatches: transactionBatches, - NotSubmittedTransactions: notSubmittedTransactions, - }, nil -} - -func batchTransactions( - ctx context.Context, - total []settlement.AggregateTransaction, -) [][]settlement.AggregateTransaction { - vpr := viper.GetViper() - chunkSize := float64(vpr.GetInt("chunk-size")) - logger := logging.FromContext(ctx) - chunked := [][]settlement.AggregateTransaction{} - - if chunkSize <= 1 { - chunkSize = 10 - } - - inner := 0 - for _, agg := range total { - inner += len(agg.Inputs) - } - - length := float64(len(total)) - logger.Info().Float64("ChunkSize", chunkSize).Float64("Total", length).Int("inner count", inner).Msg("Chunking transactions") - - for i := float64(0); i < math.Ceil(length/chunkSize); i++ { - start := i * chunkSize - if start > length { - break - } - end := start + chunkSize - if end > length { - end = length - } - - chunked = append(chunked, total[int(start):int(end)]) - } - - logger.Info().Int("Chunks", len(chunked)).Msg("Chunked transactions") - return chunked -} - -// PreparedTransactions are the transactions which have been prepared into batches after applying limits, etc -type PreparedTransactions struct { - // goes to bitflyer - AggregateTransactionBatches [][]settlement.AggregateTransaction `json:"aggregateTransactionBatches"` - // a list of settlements that are not being sent - NotSubmittedTransactions []custodian.Transaction `json:"notSubmittedTransactions"` -} - -// IterateRequest iterates requests -func IterateRequest( - ctx context.Context, - action string, - bitflyerClient bitflyer.Client, - prepared PreparedTransactions, - dryRun *bitflyer.DryRunOption, -) (map[string][]custodian.Transaction, error) { - logger := logging.FromContext(ctx) - transactionBatches := prepared.AggregateTransactionBatches - notSubmittedTransactions := prepared.NotSubmittedTransactions - - // for submission to eyeshade - submittedTransactions := make(map[string][]custodian.Transaction) - - if len(notSubmittedTransactions) > 0 { - submittedTransactions[notSubmittedCategory] = append( - submittedTransactions[notSubmittedCategory], - notSubmittedTransactions..., - ) - } - - for i, batch := range transactionBatches { - var totalValue decimal.Decimal = decimal.Zero - for j, tx := range batch { - tx.ProviderID = tx.BitflyerTransferID() - batch[j] = tx - totalValue = totalValue.Add(tx.Amount) - } - transactionBatches[i] = batch - - // this will only fetch a new quote when needed - but ensures that we don't have problems due to quote expiring midway through - quote, err := bitflyerClient.FetchQuote(ctx, "BAT_JPY", true) - if err != nil { - return nil, err - } - - request, err := CreateBitflyerRequest( - dryRun, - quote.PriceToken, - batch, - ) - if err != nil { - return nil, err - } - - if action == "upload" { - inv, err := bitflyerClient.CheckInventory(ctx) - if err != nil { - return nil, err - } - threshold, err := decimal.NewFromString("0.9") - if err != nil { - return nil, err - } - logger.Info().Str("Required Funds", totalValue.String()).Str("available", inv["BAT"].Available.String()).Msg("Will continue if within threshold") - if inv["BAT"].Available.Mul(threshold).LessThan(totalValue) { - err = errors.New("not enough balance in account") - logger.Error().Err(err).Msg("failed to submit bulk payout transactions due to insufficient available funds") - return nil, err - } - - submittedTransactions, err = SubmitBulkPayoutTransactions( - ctx, - transactionBatches[i], - submittedTransactions, - *request, - bitflyerClient, - len(transactionBatches), - i+1, - ) - if err != nil { - logger.Error().Err(err).Msg("failed to submit bulk payout transactions") - return nil, err - } - } else if action == "checkstatus" { - submittedTransactions, err = CheckPayoutTransactionsStatus( - ctx, - transactionBatches[i], - submittedTransactions, - *request, - bitflyerClient, - len(transactionBatches), - i+1, - ) - if err != nil { - logger.Error().Err(err).Msg("falied to check payout transactions status") - return nil, err - } - } - } - - return submittedTransactions, nil -} diff --git a/tools/settlement/bitflyer/upload_mock_test.go b/tools/settlement/bitflyer/upload_mock_test.go deleted file mode 100644 index 3a8960094..000000000 --- a/tools/settlement/bitflyer/upload_mock_test.go +++ /dev/null @@ -1,652 +0,0 @@ -//go:build integration -// +build integration - -package bitflyersettlement - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "os" - "strings" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/bitflyer" - mockbitflyer "github.com/brave-intl/bat-go/libs/clients/bitflyer/mock" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/golang/mock/gomock" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type BitflyerMockSuite struct { - suite.Suite - client *mockbitflyer.MockClient - token string - ctrl *gomock.Controller -} - -func (suite *BitflyerMockSuite) SetupSuite() { - mockCtrl := gomock.NewController(suite.T()) - suite.token = os.Getenv("BITFLYER_TOKEN") - suite.ctrl = mockCtrl - suite.client = mockbitflyer.NewMockClient(mockCtrl) -} - -func (suite *BitflyerMockSuite) SetupTest() { -} - -func (suite *BitflyerMockSuite) TearDownSuite() { - suite.ctrl.Finish() -} - -func (suite *BitflyerMockSuite) TearDownTest() { -} - -func (suite *BitflyerMockSuite) CleanDB() { -} - -func TestBitflyerMockSuite(t *testing.T) { - suite.Run(t, new(BitflyerMockSuite)) -} - -func (suite *BitflyerMockSuite) TestFailures() { - ctx := context.Background() - price := decimal.NewFromFloat(0.25) - amount := decimal.NewFromFloat(1.9) - amountAsFloat, _ := amount.Float64() - settlementTx0 := settlementTransaction(amount.String(), uuid.NewV4().String()) - priceToken := uuid.NewV4() - JPY := "JPY" - BAT := "BAT" - currencyCode := fmt.Sprintf("%s_%s", BAT, JPY) - sourceFrom := "tipping" - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - - preparedTransactions, err := PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx0}, - false, - "tipping", - ) - - suite.Require().NoError(err) - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - - suite.client.EXPECT(). - CheckInventory(ctx). - Return(map[string]bitflyer.Inventory{ - "BAT": bitflyer.Inventory{ - CurrencyCode: "BAT", - Amount: decimal.NewFromFloat(4.1), - Available: decimal.NewFromFloat(4.1), - }, - }, nil) - suite.client.EXPECT(). - CheckInventory(ctx). - Return(map[string]bitflyer.Inventory{ - "BAT": bitflyer.Inventory{ - CurrencyCode: "BAT", - Amount: decimal.NewFromFloat(2.2), - Available: decimal.NewFromFloat(2.2), - }, - }, nil) - - withdrawToDepositIDBulkPayload := bitflyer.NewWithdrawToDepositIDBulkPayload( - nil, - priceToken.String(), - &[]bitflyer.WithdrawToDepositIDPayload{{ - CurrencyCode: BAT, - Amount: amountAsFloat, - DepositID: settlementTx0.Destination, - TransferID: settlementTx0.BitflyerTransferID(), - SourceFrom: sourceFrom, - }}, - ) - suite.client.EXPECT(). - UploadBulkPayout( - ctx, - *withdrawToDepositIDBulkPayload, - ). - Return(&bitflyer.WithdrawToDepositIDBulkResponse{ - DryRun: false, - Withdrawals: []bitflyer.WithdrawToDepositIDResponse{{ - CurrencyCode: currencyCode, - Amount: price, - Status: "NOT_FOUND", - TransferID: settlementTx0.BitflyerTransferID(), - }}, - }, nil) - payoutFiles, err := IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - completeTxs := payoutFiles["complete"] - suite.Require().Len(completeTxs, 0, "one tx complete") - failedTxs := payoutFiles["failed"] - suite.Require().Len(failedTxs, 1, "one tx failed") - - failedBytes, err := json.Marshal(failedTxs) - settlementTx0.ProviderID = settlementTx0.TransferID() - failedTxNote := failedTxs[0].Note - suite.Require().True(strings.Contains(failedTxNote, "NOT_FOUND")) - expectedBytes, err := json.Marshal([]custodian.Transaction{ // serialize for comparison (decimal.Decimal does not do so well) - transactionSubmitted("failed", settlementTx0, failedTxNote), - }) - suite.Require().NoError(err) - suite.Require().JSONEq( - string(expectedBytes), - string(failedBytes), - "dry runs only pass through validation currently", - ) - - suite.client.EXPECT().SetAuthToken("") - suite.client.SetAuthToken("") - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - withdrawToDepositIDBulkPayload = bitflyer.NewWithdrawToDepositIDBulkPayload( - nil, - priceToken.String(), - &[]bitflyer.WithdrawToDepositIDPayload{{ - CurrencyCode: BAT, - Amount: amountAsFloat, - DepositID: settlementTx0.Destination, - TransferID: settlementTx0.BitflyerTransferID(), - SourceFrom: sourceFrom, - }}, - ) - suite.client.EXPECT(). - UploadBulkPayout( - ctx, - *withdrawToDepositIDBulkPayload, - ). - Return(nil, &clients.BitflyerError{ - Message: uuid.NewV4().String(), - ErrorIDs: []string{"1234"}, - Label: "JsonError.TOKEN_ERROR", - Status: -1, - }) - - _, err = IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, // dry run first - ) - suite.client.EXPECT().SetAuthToken(suite.token) - suite.client.SetAuthToken(suite.token) - suite.Require().Error(err) - - var bfErr *clients.BitflyerError - ok := errors.As(err, &bfErr) - suite.Require().True(ok) - errSerialized, err := json.Marshal(bfErr) - suite.Require().JSONEq( - fmt.Sprintf(`{ - "message": "%s", - "label": "JsonError.TOKEN_ERROR", - "status": -1, - "errors": ["%s"] - }`, bfErr.Message, bfErr.ErrorIDs[0]), - string(errSerialized), - ) -} - -func (suite *BitflyerMockSuite) TestFormData() { - // suite.T().Skip("bitflyer side unable to settle") - ctx := context.Background() - address := "2492cdba-d33c-4a8d-ae5d-8799a81c61c2" - sourceFrom := "tipping" - priceToken := uuid.NewV4() - JPY := "JPY" - BAT := "BAT" - currencyCode := fmt.Sprintf("%s_%s", BAT, JPY) - price := decimal.NewFromFloat(0.25) - amount := decimal.NewFromFloat(1.9) - amountAsFloat, _ := amount.Float64() - duration, err := time.ParseDuration("4s") - suite.Require().NoError(err) - dryRunOptions := &bitflyer.DryRunOption{ - ProcessTimeSec: uint(duration.Seconds()), - } - - settlementTx1 := settlementTransaction(amount.String(), address) - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - - preparedTransactions, err := PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx1}, - false, - sourceFrom, - ) - suite.Require().NoError(err) - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - suite.client.EXPECT(). - CheckInventory(ctx). - Return(map[string]bitflyer.Inventory{ - "BAT": bitflyer.Inventory{ - CurrencyCode: "BAT", - Amount: decimal.NewFromFloat(4.1), - Available: decimal.NewFromFloat(4.1), - }, - }, nil) - suite.client.EXPECT(). - CheckInventory(ctx). - Return(map[string]bitflyer.Inventory{ - "BAT": bitflyer.Inventory{ - CurrencyCode: "BAT", - Amount: decimal.NewFromFloat(2.2), - Available: decimal.NewFromFloat(2.2), - }, - }, nil) - - withdrawToDepositIDBulkPayload := bitflyer.NewWithdrawToDepositIDBulkPayload( - dryRunOptions, - priceToken.String(), - &[]bitflyer.WithdrawToDepositIDPayload{{ - CurrencyCode: BAT, - Amount: amountAsFloat, - DepositID: address, - TransferID: settlementTx1.BitflyerTransferID(), - SourceFrom: sourceFrom, - }}, - ) - suite.client.EXPECT(). - UploadBulkPayout( - ctx, - *withdrawToDepositIDBulkPayload, - ). - Return(&bitflyer.WithdrawToDepositIDBulkResponse{ - DryRun: true, - Withdrawals: []bitflyer.WithdrawToDepositIDResponse{{ - CurrencyCode: currencyCode, - Amount: amount, - Status: "SUCCESS", - TransferID: settlementTx1.BitflyerTransferID(), - }}, - }, nil) - - payoutFiles, err := IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - dryRunOptions, // dry run first - ) - - suite.Require().NoError(err) - completedDryRunTxs := payoutFiles["complete"] - suite.Require().Len(completedDryRunTxs, 1, "one transaction should be created") - - completedDryRunBytes, err := json.Marshal(completedDryRunTxs) - suite.Require().NoError(err) - - settlementTx1.ProviderID = settlementTx1.TransferID() - expectedBytes, err := json.Marshal([]custodian.Transaction{ // serialize for comparison (decimal.Decimal does not do so well) - transactionSubmitted("complete", settlementTx1, "SUCCESS transferID: "+settlementTx1.BitflyerTransferID()), - }) - suite.Require().JSONEq( - string(expectedBytes), - string(completedDryRunBytes), - "dry runs only pass through validation currently", - ) - dryRunOptions.ProcessTimeSec = 0 - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - withdrawToDepositIDBulkPayload = bitflyer.NewWithdrawToDepositIDBulkPayload( - nil, - priceToken.String(), - &[]bitflyer.WithdrawToDepositIDPayload{{ - CurrencyCode: BAT, - Amount: amountAsFloat, - DepositID: address, - TransferID: settlementTx1.BitflyerTransferID(), - SourceFrom: sourceFrom, - }}, - ) - suite.client.EXPECT(). - CheckInventory(ctx). - Return(map[string]bitflyer.Inventory{ - "BAT": bitflyer.Inventory{ - CurrencyCode: "BAT", - Amount: decimal.NewFromFloat(3.2), - Available: decimal.NewFromFloat(3.2), - }, - }, nil) - - suite.client.EXPECT(). - UploadBulkPayout( - ctx, - *withdrawToDepositIDBulkPayload, - ). - Return(&bitflyer.WithdrawToDepositIDBulkResponse{ - DryRun: true, - Withdrawals: []bitflyer.WithdrawToDepositIDResponse{{ - CurrencyCode: currencyCode, - Amount: amount, - Status: "SUCCESS", - TransferID: settlementTx1.BitflyerTransferID(), - }}, - }, nil) - - payoutFiles, err = IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - // setting an array on the "complete" key means we will have a file written - // with the suffix of "complete" when this function is called in the cli scripts - completed := payoutFiles["complete"] - suite.Require().Len(completed, 1, "one transaction should be created") - completeSerialized, err := json.Marshal(completed) - suite.Require().NoError(err) - - settlementTx1.ProviderID = settlementTx1.TransferID() // add bitflyer transaction hash - mCompleted, err := json.Marshal([]custodian.Transaction{ // serialize for comparison (decimal.Decimal does not do so well) - transactionSubmitted("complete", settlementTx1, "SUCCESS transferID: "+settlementTx1.BitflyerTransferID()), - }) - suite.Require().NoError(err) - suite.Require().JSONEq( - string(completeSerialized), - string(mCompleted), - ) - var completedStatus []custodian.Transaction - for { - <-time.After(time.Second) - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: "JPY", - SubCurrency: "BAT", - Rate: price, - }, nil) - - suite.client.EXPECT(). - CheckPayoutStatus( - ctx, - bitflyer.NewWithdrawToDepositIDBulkPayload( - nil, - priceToken.String(), - &[]bitflyer.WithdrawToDepositIDPayload{{ - CurrencyCode: BAT, - Amount: amountAsFloat, - DepositID: address, - TransferID: settlementTx1.BitflyerTransferID(), - SourceFrom: sourceFrom, - }}, - ).ToBulkStatus(), - ). - Return(&bitflyer.WithdrawToDepositIDBulkResponse{ - DryRun: true, - Withdrawals: []bitflyer.WithdrawToDepositIDResponse{{ - Status: "EXECUTED", - TransferID: settlementTx1.BitflyerTransferID(), - }}, - }, nil) - - payoutFiles, err = IterateRequest( - ctx, - "checkstatus", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - completedStatus = payoutFiles["complete"] - // useful if the loop never finishes - if len(completedStatus) > 0 { - break - } - } - suite.Require().Len(completedStatus, 1, "one transaction should be created") - completeSerializedStatus, err := json.Marshal(completedStatus) - suite.Require().NoError(err) - - mCompletedStatus, err := json.Marshal([]custodian.Transaction{ - transactionSubmitted("complete", settlementTx1, "EXECUTED transferID: "+settlementTx1.BitflyerTransferID()), - }) - suite.Require().NoError(err) - suite.Require().JSONEq(string(mCompletedStatus), string(completeSerializedStatus)) - - // make a new tx that will conflict with previous - three := decimal.NewFromFloat(2.85) - threeAsFloat, _ := three.Float64() - settlementTx2 := settlementTransaction(three.String(), address) - settlementTx2.SettlementID = settlementTx1.SettlementID - settlementTx2.Destination = settlementTx1.Destination - settlementTx2.WalletProviderID = settlementTx1.WalletProviderID - settlementTx2.ProviderID = settlementTx2.BitflyerTransferID() // add bitflyer transaction hash - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - - preparedTransactions, err = PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx2}, - false, - sourceFrom, - ) - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - withdrawToDepositIDBulkPayload = bitflyer.NewWithdrawToDepositIDBulkPayload( - nil, - priceToken.String(), - &[]bitflyer.WithdrawToDepositIDPayload{{ - CurrencyCode: BAT, - Amount: threeAsFloat, - DepositID: address, - TransferID: settlementTx2.BitflyerTransferID(), - SourceFrom: sourceFrom, - }}, - ) - suite.client.EXPECT(). - UploadBulkPayout( - ctx, - *withdrawToDepositIDBulkPayload, - ). - Return(&bitflyer.WithdrawToDepositIDBulkResponse{ - DryRun: false, - Withdrawals: []bitflyer.WithdrawToDepositIDResponse{{ - CurrencyCode: currencyCode, - Amount: amount, - Message: "Duplicate transfer_id and different parameters", - Status: "OTHER_ERROR", - TransferID: settlementTx2.BitflyerTransferID(), - }}, - }, nil) - - payoutFiles, err = IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - idempotencyFailComplete := payoutFiles["failed"] - suite.Require().Len(idempotencyFailComplete, 1, "one transaction should be created") - _, err = json.Marshal(idempotencyFailComplete) - suite.Require().NoError(err) - - // bitflyer does not send us back what we sent it - // so we end up in an odd space if we change amount or other inputs - // which is ok, it just makes for odd checks - // in this particular case, we return the original transactions with an "failed" status - // which is why we do not need to modify the number amounts - // - // the invalid-input part is what will put the transaction in a different file - // so that we do not send to eyeshade - - // two := decimal.NewFromFloat(1.9) - // settlementTx2.Amount = two - // settlementTx2.Probi = altcurrency.BAT.ToProbi(settlementTx2.Amount) - // settlementTx2.BATPlatformFee = altcurrency.BAT.ToProbi(decimal.NewFromFloat(0.1)) - - // idempotencyFailNote := idempotencyFailComplete[0].Note - // suite.Require().Equal("OTHER_ERROR: Duplicate transfer_id and different parameters. transferID: "+idempotencyFailComplete[0].BitflyerTransferID(), idempotencyFailNote) - // idempotencyFailCompleteExpected, err := json.Marshal([]custodian.Transaction{ - // transactionSubmitted("complete", settlementTx2, idempotencyFailNote), - // }) - // suite.Require().NoError(err) - // suite.Require().JSONEq( - // string(idempotencyFailCompleteExpected), - // string(idempotencyFailCompleteActual), - // ) -} - -func (suite *BitflyerMockSuite) TestPrepareRequests() { - priceToken := uuid.NewV4() - JPY := "JPY" - BAT := "BAT" - currencyCode := fmt.Sprintf("%s_%s", BAT, JPY) - price := decimal.NewFromFloat(0.25) - - ctx := context.Background() - - address1 := uuid.NewV4() - address2 := uuid.NewV4() - address3 := uuid.NewV4() - - settlementTx1 := settlementTransaction("1.9", address1.String()) - settlementTx2 := settlementTransaction("1.3", address1.String()) - settlementTx3 := settlementTransaction("1.1", address2.String()) - settlementTx4 := settlementTransaction("9999999999999", address3.String()) - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - - preparedTransactions, err := PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx1, settlementTx2, settlementTx3, settlementTx4}, - false, - "tipping", - ) - suite.Require().NoError(err) - - totalTxns := 0 - for _, batches := range preparedTransactions.AggregateTransactionBatches { - totalTxns += len(batches) - } - - suite.Require().Equal(3, totalTxns, "three agrregated transactions should be prepared") - suite.Require().Len(preparedTransactions.NotSubmittedTransactions, 0, "zero transaction should be skipped") - - suite.client.EXPECT(). - FetchQuote(ctx, currencyCode, true). - Return(&bitflyer.Quote{ - PriceToken: priceToken.String(), - ProductCode: currencyCode, - MainCurrency: JPY, - SubCurrency: BAT, - Rate: price, - }, nil) - - preparedTransactions, err = PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx1, settlementTx2, settlementTx3, settlementTx4}, - true, - "tipping", - ) - suite.Require().NoError(err) - - totalTxns = 0 - for _, batches := range preparedTransactions.AggregateTransactionBatches { - totalTxns += len(batches) - } - suite.Require().Equal(2, totalTxns, "two aggregated transaction should be prepared") - suite.Require().Len(preparedTransactions.NotSubmittedTransactions, 1, "one transaction should be skipped") - -} diff --git a/tools/settlement/bitflyer/upload_test.go b/tools/settlement/bitflyer/upload_test.go deleted file mode 100644 index f88c9206f..000000000 --- a/tools/settlement/bitflyer/upload_test.go +++ /dev/null @@ -1,344 +0,0 @@ -//go:build integration -// +build integration - -package bitflyersettlement - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "os" - "strings" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients" - "github.com/brave-intl/bat-go/libs/clients/bitflyer" - "github.com/brave-intl/bat-go/libs/custodian" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/suite" -) - -type BitflyerSuite struct { - suite.Suite - client bitflyer.Client - token string -} - -func (suite *BitflyerSuite) SetupSuite() { - if os.Getenv("BITFLYER_LIVE") != "true" { - suite.T().Skip("bitflyer side unable to settle") - } - client, err := bitflyer.New() - suite.client = client - suite.Require().NoError(err) - token := os.Getenv("BITFLYER_TOKEN") - if token == "" { - payload := bitflyer.TokenPayload{ - GrantType: "client_credentials", - ClientID: os.Getenv("BITFLYER_CLIENT_ID"), - ClientSecret: os.Getenv("BITFLYER_CLIENT_SECRET"), - ExtraClientSecret: os.Getenv("BITFLYER_EXTRA_CLIENT_SECRET"), - } - auth, err := client.RefreshToken( - context.Background(), - payload, - ) - suite.Require().NoError(err) - suite.token = auth.AccessToken - client.SetAuthToken(auth.AccessToken) - } else { - suite.token = token - } -} - -func (suite *BitflyerSuite) SetupTest() { -} - -func (suite *BitflyerSuite) TearDownTest() { -} - -func (suite *BitflyerSuite) CleanDB() { -} - -func TestBitflyerSuite(t *testing.T) { - suite.Run(t, new(BitflyerSuite)) -} - -func settlementTransaction(amount, address string) custodian.Transaction { - amountDecimal, _ := decimal.NewFromString(amount) - bat := altcurrency.BAT - fees := amountDecimal.Div(decimal.NewFromFloat(19)) - settlementID := uuid.NewV4().String() - tx := custodian.Transaction{ - AltCurrency: &bat, - Currency: "BAT", - Amount: amountDecimal, - Probi: amountDecimal.Mul(decimal.New(1, 18)), - BATPlatformFee: fees.Mul(decimal.New(1, 18)).Truncate(18), - Destination: address, - Type: "contribution", - SettlementID: settlementID, - WalletProvider: "bitflyer", - WalletProviderID: address, - TransferFee: decimal.NewFromFloat(0), - ExchangeFee: decimal.NewFromFloat(0), - Channel: "brave.com", - } - tx.ProviderID = tx.TransferID() - return tx -} - -func transactionSubmitted(status string, tx custodian.Transaction, note string) custodian.Transaction { - return custodian.Transaction{ - Status: status, - Channel: tx.Channel, - AltCurrency: tx.AltCurrency, - Currency: tx.Currency, - Type: tx.Type, - ProviderID: tx.ProviderID, - Amount: tx.Amount, - Probi: tx.Probi, - BATPlatformFee: tx.BATPlatformFee, - Destination: tx.Destination, - SettlementID: tx.SettlementID, - WalletProvider: tx.WalletProvider, - WalletProviderID: tx.WalletProviderID, - ExchangeFee: tx.ExchangeFee, - TransferFee: tx.TransferFee, - Note: note, - } -} - -func (suite *BitflyerSuite) TestFailures() { - ctx := context.Background() - settlementTx0 := settlementTransaction("1.9", uuid.NewV4().String()) - - preparedTransactions, err := PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx0}, - false, - "tipping", - ) - - payoutFiles, err := IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - completeTxs := payoutFiles["complete"] - suite.Require().Len(completeTxs, 0, "one tx complete") - failedTxs := payoutFiles["failed"] - suite.Require().Len(failedTxs, 1, "one tx failed") - - failedBytes, err := json.Marshal(failedTxs) - settlementTx0.ProviderID = settlementTx0.BitflyerTransferID() - failedTxNote := failedTxs[0].Note - fmt.Printf("%#v\n", failedTxNote) - suite.Require().True(strings.Contains(failedTxNote, "NOT_FOUND")) - expectedBytes, err := json.Marshal([]custodian.Transaction{ // serialize for comparison (decimal.Decimal does not do so well) - transactionSubmitted("failed", settlementTx0, failedTxNote), - }) - suite.Require().NoError(err) - suite.Require().JSONEq( - string(expectedBytes), - string(failedBytes), - "dry runs only pass through validation currently", - ) - - suite.client.SetAuthToken("") - _, err = IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, // dry run first - ) - suite.client.SetAuthToken(suite.token) - suite.Require().Error(err) - var bfErr *clients.BitflyerError - ok := errors.As(err, &bfErr) - suite.Require().True(ok) - errSerialized, err := json.Marshal(bfErr) - suite.Require().JSONEq( - fmt.Sprintf(`{ - "message": "%s", - "label": "JsonError.TOKEN_ERROR", - "status": -1, - "errors": ["%s"] - }`, bfErr.Message, bfErr.ErrorIDs[0]), - string(errSerialized), - ) -} - -func (suite *BitflyerSuite) TestFormData() { - // TODO: after we figure out why we are being blocked by bf enable - ctx := context.Background() - address := "ff3a0ead-c945-4c52-bcf7-9309319573de" - sourceFrom := "tipping" - duration, err := time.ParseDuration("4s") - suite.Require().NoError(err) - dryRunOptions := &bitflyer.DryRunOption{ - ProcessTimeSec: uint(duration.Seconds()), - } - - settlementTx1 := settlementTransaction("1.9", address) - - preparedTransactions, err := PrepareRequests( - ctx, - suite.client, - []custodian.Transaction{settlementTx1}, - false, - sourceFrom, - ) - /* - resultIteration := make(map[string]int) - - var payoutFiles *map[string][]custodian.Transaction - for i := 0; i < 2; i++ { - <-time.After(2 * time.Second) - results, err := IterateRequest( - ctx, - "upload", - suite.client, - []string{tmpFile1.Name()}, - sourceFrom, - false, - dryRunOptions, // dry run first - ) - suite.Require().NoError(err) - for key, items := range *results { - resultIteration[key] += len(items) - } - payoutFiles = results - } - suite.Require().Equal(map[string]int{ - "pending": 1, - "complete": 1, - }, resultIteration) - */ - - payoutFiles, err := IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - dryRunOptions, // dry run first - ) - suite.Require().NoError(err) - completedDryRunTxs := payoutFiles["complete"] - suite.Require().Len(completedDryRunTxs, 1, "one transaction should be created") - - completedDryRunBytes, err := json.Marshal(completedDryRunTxs) - suite.Require().NoError(err) - - settlementTx1.ProviderID = settlementTx1.BitflyerTransferID() - expectedBytes, err := json.Marshal([]custodian.Transaction{ // serialize for comparison (decimal.Decimal does not do so well) - transactionSubmitted("complete", settlementTx1, "SUCCESS"), - }) - suite.Require().JSONEq( - string(completedDryRunBytes), - string(expectedBytes), - "dry runs only pass through validation currently", - ) - dryRunOptions.ProcessTimeSec = 0 - - payoutFiles, err = IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - // setting an array on the "complete" key means we will have a file written - // with the suffix of "complete" when this function is called in the cli scripts - completed := payoutFiles["complete"] - suite.Require().Len(completed, 1, "one transaction should be created") - completeSerialized, err := json.Marshal(completed) - suite.Require().NoError(err) - - settlementTx1.ProviderID = settlementTx1.BitflyerTransferID() // add bitflyer transaction hash - mCompleted, err := json.Marshal([]custodian.Transaction{ // serialize for comparison (decimal.Decimal does not do so well) - transactionSubmitted("complete", settlementTx1, "SUCCESS"), - }) - suite.Require().NoError(err) - suite.Require().JSONEq( - string(completeSerialized), - string(mCompleted), - ) - - var completedStatus []custodian.Transaction - for { - <-time.After(time.Second) - payoutFiles, err = IterateRequest( - ctx, - "checkstatus", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - completedStatus = payoutFiles["complete"] - // useful if the loop never finishes - // fmt.Printf("checkstatus %#v\n", *payoutFiles) - if len(completedStatus) > 0 { - break - } - } - suite.Require().Len(completedStatus, 1, "one transaction should be created") - completeSerializedStatus, err := json.Marshal(completedStatus) - suite.Require().NoError(err) - - mCompletedStatus, err := json.Marshal([]custodian.Transaction{ - transactionSubmitted("complete", settlementTx1, "EXECUTED"), - }) - suite.Require().NoError(err) - suite.Require().JSONEq(string(completeSerializedStatus), string(mCompletedStatus)) - - // make a new tx that will conflict with previous - settlementTx2 := settlementTransaction("2.85", address) - settlementTx2.SettlementID = settlementTx1.SettlementID - settlementTx2.Destination = settlementTx1.Destination - settlementTx2.WalletProviderID = settlementTx1.WalletProviderID - settlementTx2.ProviderID = settlementTx2.BitflyerTransferID() // add bitflyer transaction hash - - payoutFiles, err = IterateRequest( - ctx, - "upload", - suite.client, - *preparedTransactions, - nil, - ) - suite.Require().NoError(err) - idempotencyFailComplete := payoutFiles["complete"] - suite.Require().Len(idempotencyFailComplete, 1, "one transaction should be created") - idempotencyFailCompleteActual, err := json.Marshal(idempotencyFailComplete) - suite.Require().NoError(err) - - // bitflyer does not send us back what we sent it - // so we end up in an odd space if we change amount or other inputs - // which is ok, it just makes for odd checks - // in this particular case, we return the original transactions with an "failed" status - // which is why we do not need to modify the number amounts - // - // the invalid-input part is what will put the transaction in a different file - // so that we do not send to eyeshade - idempotencyFailNote := idempotencyFailComplete[0].Note - suite.Require().Equal("OTHER_ERROR: Duplicate transfer_id and different parameters", idempotencyFailNote) - idempotencyFailCompleteExpected, err := json.Marshal([]custodian.Transaction{ - transactionSubmitted("failed", settlementTx2, idempotencyFailNote), - }) - suite.Require().NoError(err) - suite.Require().JSONEq( - string(idempotencyFailCompleteExpected), - string(idempotencyFailCompleteActual), - ) -} diff --git a/tools/settlement/cmd/bitflyer.go b/tools/settlement/cmd/bitflyer.go deleted file mode 100644 index f7eeb1fe7..000000000 --- a/tools/settlement/cmd/bitflyer.go +++ /dev/null @@ -1,318 +0,0 @@ -package settlement - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "path/filepath" - "strings" - "time" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/clients/bitflyer" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/logging" - bitflyersettlement "github.com/brave-intl/bat-go/tools/settlement/bitflyer" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - // BitflyerSettlementCmd creates the bitflyer subcommand - BitflyerSettlementCmd = &cobra.Command{ - Use: "bitflyer", - Short: "facilitates bitflyer settlement", - } - - // UploadBitflyerSettlementCmd creates the bitflyer uphold subcommand - UploadBitflyerSettlementCmd = &cobra.Command{ - Use: "upload", - Short: "uploads signed bitflyer transactions", - Run: rootcmd.Perform("bitflyer upload", UploadBitflyerSettlement), - } - - // CheckStatusBitflyerSettlementCmd creates the bitflyer checkstatus subcommand - CheckStatusBitflyerSettlementCmd = &cobra.Command{ - Use: "checkstatus", - Short: "uploads signed bitflyer transactions", - Run: rootcmd.Perform("bitflyer checkstatus", CheckStatusBitflyerSettlement), - } - - // GetBitflyerTokenCmd gets a new bitflyer token - GetBitflyerTokenCmd = &cobra.Command{ - Use: "token", - Short: "gets a new token for authing", - Run: rootcmd.Perform("bitflyer token", GetBitflyerToken), - } -) - -// NewRefreshTokenPayloadFromViper creates the payload to refresh a bitflyer token from viper -func NewRefreshTokenPayloadFromViper() *bitflyer.TokenPayload { - vpr := viper.GetViper() - clientID := vpr.GetString("bitflyer-client-id") - clientSecret := vpr.GetString("bitflyer-client-secret") - extraClientSecret := vpr.GetString("bitflyer-extra-client-secret") - return &bitflyer.TokenPayload{ - GrantType: "client_credentials", - ClientID: clientID, - ClientSecret: clientSecret, - ExtraClientSecret: extraClientSecret, - } -} - -// GetBitflyerToken gets a new bitflyer token from cobra command -func GetBitflyerToken(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - refreshTokenPayload := NewRefreshTokenPayloadFromViper() - client, err := bitflyer.New() - if err != nil { - return err - } - auth, err := client.RefreshToken( - ctx, - *refreshTokenPayload, - ) - if err != nil { - return err - } - logger.Info(). - Str("access_token", auth.AccessToken). - Int("expires_in", auth.ExpiresIn). - Str("refresh_token", auth.RefreshToken). - Str("scope", auth.Scope). - Str("token_type", auth.TokenType). - Msg("token refreshed") - return nil -} - -// UploadBitflyerSettlement uploads bitflyer settlement -func UploadBitflyerSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - token := viper.GetViper().GetString("bitflyer-client-token") - if out == "" { - out = strings.TrimSuffix(input, filepath.Ext(input)) + "-finished.json" - } - excludeLimited, err := cmd.Flags().GetBool("exclude-limited") - if err != nil { - return err - } - dryRunOptions, err := ParseDryRun(cmd) - if err != nil { - return err - } - return BitflyerUploadSettlement( - cmd.Context(), - "upload", - input, - out, - token, - excludeLimited, - dryRunOptions, - ) -} - -// ParseDryRun parses the dry run option -func ParseDryRun(cmd *cobra.Command) (*bitflyer.DryRunOption, error) { - dryRun, err := cmd.Flags().GetBool("bitflyer-dryrun") - if err != nil { - return nil, err - } - var dryRunOptions *bitflyer.DryRunOption - if dryRun { - dryRunDuration, err := cmd.Flags().GetDuration("bitflyer-process-time") - if err != nil { - return nil, err - } - dryRunOptions = &bitflyer.DryRunOption{ - ProcessTimeSec: uint(dryRunDuration.Seconds()), - } - } - return dryRunOptions, nil -} - -// CheckStatusBitflyerSettlement is the command runner for checking bitflyer transactions status -func CheckStatusBitflyerSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - if out == "" { - out = strings.TrimSuffix(input, filepath.Ext(input)) + "-finished.json" - } - token := viper.GetViper().GetString("bitflyer-client-token") - excludeLimited, err := cmd.Flags().GetBool("exclude-limited") - if err != nil { - return err - } - - dryRunOptions, err := ParseDryRun(cmd) - if err != nil { - return err - } - return BitflyerUploadSettlement( - cmd.Context(), - "checkstatus", - input, - out, - token, - excludeLimited, - dryRunOptions, - ) -} - -func init() { - // add complete and transform subcommand - BitflyerSettlementCmd.AddCommand(GetBitflyerTokenCmd) - BitflyerSettlementCmd.AddCommand(UploadBitflyerSettlementCmd) - BitflyerSettlementCmd.AddCommand(CheckStatusBitflyerSettlementCmd) - - // add this command as a settlement subcommand - SettlementCmd.AddCommand(BitflyerSettlementCmd) - - // setup the flags - tokenBuilder := cmdutils.NewFlagBuilder(GetBitflyerTokenCmd) - uploadCheckStatusBuilder := cmdutils.NewFlagBuilder(UploadBitflyerSettlementCmd). - AddCommand(CheckStatusBitflyerSettlementCmd) - allBuilder := tokenBuilder.Concat(uploadCheckStatusBuilder) - - uploadCheckStatusBuilder.Flag().String("input", "", - "the file or comma delimited list of files that should be utilized. both referrals and contributions should be done in one command in order to group the transactions appropriately"). - Require(). - Bind("input") - - uploadCheckStatusBuilder.Flag().String("out", "./bitflyer-settlement", - "the location of the file"). - Bind("out"). - Env("OUT") - - uploadCheckStatusBuilder.Flag().Bool("bitflyer-dryrun", false, - "tells bitflyer that this is a practice round"). - Bind("bitflyer-dryrun"). - Env("BITFLYER_DRYRUN") - - uploadCheckStatusBuilder.Flag().Duration("bitflyer-process-time", time.Second, - "tells bitflyer the duration of this practice round"). - Bind("bitflyer-dryrun"). - Env("BITFLYER_DRYRUN") - - uploadCheckStatusBuilder.Flag().String("bitflyer-client-token", "", - "the token to be sent for auth on bitflyer"). - Bind("bitflyer-client-token"). - Env("BITFLYER_TOKEN") - - tokenBuilder.Flag().String("bitflyer-client-id", "", - "tells bitflyer what the client id is during token generation"). - Bind("bitflyer-client-id"). - Env("BITFLYER_CLIENT_ID") - - tokenBuilder.Flag().String("bitflyer-client-secret", "", - "tells bitflyer what the client secret during token generation"). - Bind("bitflyer-client-secret"). - Env("BITFLYER_CLIENT_SECRET") - - tokenBuilder.Flag().String("bitflyer-extra-client-secret", "", - "tells bitflyer what the extra client secret is during token"). - Bind("bitflyer-extra-client-secret"). - Env("BITFLYER_EXTRA_CLIENT_SECRET") - - allBuilder.Flag().String("bitflyer-server", "", - "the bitflyer domain to interact with"). - Bind("bitflyer-server"). - Env("BITFLYER_SERVER") - - allBuilder.Flag().Bool("exclude-limited", false, - "in order to avoid not knowing what the payout amount will be because of transfer limits"). - Bind("exclude-limited") - - allBuilder.Flag().String("bitflyer-source-from", "tipping", - "tells bitflyer where to draw funds from"). - Bind("bitflyer-source-from"). - Env("BITFLYER_SOURCE_FROM") -} - -// BitflyerUploadSettlement marks the settlement file as complete -func BitflyerUploadSettlement( - ctx context.Context, - action, inPath, outPath, token string, - excludeLimited bool, - dryRun *bitflyer.DryRunOption, -) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - - bitflyerClient, err := GetBitflyerAuthorizedClient(ctx, token) - if err != nil { - logger.Error().Err(err).Msg("failed to create new bitflyer client") - return err - } - - bytes, err := ioutil.ReadFile(inPath) - if err != nil { - logger.Error().Err(err).Msg("failed to read bulk payout file") - return err - } - - var preparedTransactions bitflyersettlement.PreparedTransactions - err = json.Unmarshal(bytes, &preparedTransactions) - if err != nil { - logger.Error().Err(err).Msg("failed unmarshal bulk payout file") - return err - } - - submittedTransactions, submitErr := bitflyersettlement.IterateRequest( - ctx, - action, - bitflyerClient, - preparedTransactions, - dryRun, - ) - // write file for upload to eyeshade - logger.Info(). - Str("files", outPath). - Msg("outputting files") - - err = WriteCategorizedTransactions(ctx, outPath, submittedTransactions) - if err != nil { - logger.Error().Err(err).Msg("failed to write transactions file") - return err - } - return submitErr -} - -func GetBitflyerAuthorizedClient(ctx context.Context, token string) (bitflyer.Client, error) { - bitflyerClient, err := bitflyer.New() - if err != nil { - return bitflyerClient, fmt.Errorf("failed to create new bitflyer client: %w", err) - } - // set the auth token - if token != "" { - bitflyerClient.SetAuthToken(token) - } else { - refreshTokenPayload := NewRefreshTokenPayloadFromViper() - resp, err := bitflyerClient.RefreshToken(ctx, *refreshTokenPayload) - fmt.Printf("TOKENRESP: %v\n", resp) - if err != nil { - return bitflyerClient, fmt.Errorf("failed to refresh bitflyer token: %w", err) - } - } - return bitflyerClient, nil -} diff --git a/tools/settlement/cmd/gemini.go b/tools/settlement/cmd/gemini.go deleted file mode 100644 index 6139038d6..000000000 --- a/tools/settlement/cmd/gemini.go +++ /dev/null @@ -1,223 +0,0 @@ -package settlement - -import ( - "context" - "encoding/json" - "errors" - "io/ioutil" - "os" - "path/filepath" - "strings" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/clients/gemini" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/tools/settlement" - geminisettlement "github.com/brave-intl/bat-go/tools/settlement/gemini" - "github.com/spf13/cobra" -) - -var ( - // GeminiSettlementCmd creates the gemini subcommand - GeminiSettlementCmd = &cobra.Command{ - Use: "gemini", - Short: "provides gemini settlement", - } - - // UploadGeminiSettlementCmd creates the gemini uphold subcommand - UploadGeminiSettlementCmd = &cobra.Command{ - Use: "upload", - Short: "uploads signed gemini transactions", - Run: rootcmd.Perform("gemini upload", UploadGeminiSettlement), - } - - // CheckStatusGeminiSettlementCmd creates the gemini checkstatus subcommand - CheckStatusGeminiSettlementCmd = &cobra.Command{ - Use: "checkstatus", - Short: "uploads signed gemini transactions", - Run: rootcmd.Perform("gemini checkstatus", CheckStatusGeminiSettlement), - } -) - -// UploadGeminiSettlement uploads gemini settlement -func UploadGeminiSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - sig, err := cmd.Flags().GetInt("sig") - if err != nil { - return err - } - allTransactionsFile, err := cmd.Flags().GetString("all-txs-input") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - - if out == "" { - out = strings.TrimSuffix(input, filepath.Ext(input)) + "-finished.json" - } - - ctx := context.WithValue(cmd.Context(), appctx.GeminiAPISecretCTXKey, os.Getenv("GEMINI_API_SECRET")) - ctx = context.WithValue(ctx, appctx.GeminiAPIKeyCTXKey, os.Getenv("GEMINI_API_KEY")) - - return GeminiUploadSettlement( - ctx, - "upload", - input, - sig, - allTransactionsFile, - out, - ) -} - -// CheckStatusGeminiSettlement is the command runner for checking gemini transactions status -func CheckStatusGeminiSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - if out == "" { - out = strings.TrimSuffix(input, filepath.Ext(input)) + "-finished.json" - } - sig, err := cmd.Flags().GetInt("sig") - if err != nil { - return err - } - allTxsInput, err := cmd.Flags().GetString("all-txs-input") - if err != nil { - return err - } - - ctx := context.WithValue(cmd.Context(), appctx.GeminiAPISecretCTXKey, os.Getenv("GEMINI_API_SECRET")) - ctx = context.WithValue(ctx, appctx.GeminiAPIKeyCTXKey, os.Getenv("GEMINI_API_KEY")) - - return GeminiUploadSettlement( - ctx, - "checkstatus", - input, - sig, - allTxsInput, - out, - ) -} - -func init() { - // add complete and transform subcommand - GeminiSettlementCmd.AddCommand(UploadGeminiSettlementCmd) - GeminiSettlementCmd.AddCommand(CheckStatusGeminiSettlementCmd) - - // add this command as a settlement subcommand - SettlementCmd.AddCommand(GeminiSettlementCmd) - - // setup the flags - uploadCheckStatusBuilder := cmdutils.NewFlagBuilder(UploadGeminiSettlementCmd). - AddCommand(CheckStatusGeminiSettlementCmd) - - uploadCheckStatusBuilder.Flag().String("input", "", - "the file or comma delimited list of files that should be utilized"). - Require(). - Bind("input"). - Env("INPUT") - - uploadCheckStatusBuilder.Flag().String("out", "./gemini-settlement", - "the location of the file"). - Bind("out"). - Env("OUT") - - uploadCheckStatusBuilder.Flag().String("all-txs-input", "", - "the original transactions file"). - Bind("all-txs-input"). - Require() - - uploadCheckStatusBuilder.Flag().Int("sig", 0, - "signature to choose when uploading transactions (for bulk endpoint usage)"). - Bind("sig") -} - -// GeminiUploadSettlement marks the settlement file as complete -func GeminiUploadSettlement(ctx context.Context, action string, inPath string, signatureSwitch int, allTransactionsFile string, outPath string) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - - if outPath == "./gemini-settlement" { - // use a file with extension if none is passed - outPath = "./gemini-settlement-complete.json" - } - - bulkPayoutFiles := strings.Split(inPath, ",") - geminiClient, err := gemini.New() - if err != nil { - logger.Error().Err(err).Msg("failed to create new gemini client") - return err - } - - if allTransactionsFile == "" { - logger.Error().Msg("transactions file is empty") - return errors.New("unable to upload without a transactions file to check against") - } - - bytes, err := ioutil.ReadFile(allTransactionsFile) - if err != nil { - logger.Error().Err(err).Msg("failed to read the transactions file") - return err - } - - var settlementTransactions []settlement.AntifraudTransaction - err = json.Unmarshal(bytes, &settlementTransactions) - if err != nil { - logger.Error().Err(err).Msg("failed to unmarshal the transactions file") - return err - } - // create a map of the request transactions - transactionsMap, err := geminiMapTransactionsToID(settlementTransactions) - if err != nil { - logger.Error().Err(err).Msg("failed validate and convert transactions") - return err - } - - submittedTransactions, submitErr := geminisettlement.IterateRequest( - ctx, - action, - geminiClient, - signatureSwitch, - bulkPayoutFiles, - transactionsMap, - ) - // write file for upload to eyeshade - logger.Info(). - Str("files", outPath). - Msg("outputting files") - - err = WriteCategorizedTransactions(ctx, outPath, submittedTransactions) - if err != nil { - return err - } - return submitErr -} - -// geminiMapTransactionsToID creates a map of guid's to transactions -func geminiMapTransactionsToID(transactions []settlement.AntifraudTransaction) (map[string]custodian.Transaction, error) { - transactionsMap := make(map[string]custodian.Transaction) - for _, atx := range transactions { - tx, err := atx.ToTransaction() - if err != nil { - return transactionsMap, err - } - transactionsMap[gemini.GenerateTxRef(&tx)] = tx - } - return transactionsMap, nil -} diff --git a/tools/settlement/cmd/paypal.go b/tools/settlement/cmd/paypal.go deleted file mode 100644 index c32381dfd..000000000 --- a/tools/settlement/cmd/paypal.go +++ /dev/null @@ -1,323 +0,0 @@ -package settlement - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "strings" - "text/template" - "time" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/custodian" - - "github.com/brave-intl/bat-go/libs/closers" - "github.com/brave-intl/bat-go/tools/settlement" - "github.com/brave-intl/bat-go/tools/settlement/paypal" - "github.com/gocarina/gocsv" - "github.com/rs/zerolog" - "github.com/shopspring/decimal" - "github.com/spf13/cobra" -) - -func init() { - // add complete and transform subcommand - PaypalSettlementCmd.AddCommand(CompletePaypalSettlementCmd) - PaypalSettlementCmd.AddCommand(TransformPaypalSettlementCmd) - PaypalSettlementCmd.AddCommand(EmailPaypalSettlementCmd) - - // add this command as a settlement subcommand - SettlementCmd.AddCommand(PaypalSettlementCmd) - - // setup the flags - completeBuilder := cmdutils.NewFlagBuilder(CompletePaypalSettlementCmd) - transformBuilder := cmdutils.NewFlagBuilder(TransformPaypalSettlementCmd) - emailBuilder := cmdutils.NewFlagBuilder(EmailPaypalSettlementCmd) - transformEmailCompleteBuilder := completeBuilder.Concat(transformBuilder, emailBuilder) - - transformEmailCompleteBuilder.Flag().String("input", "", - "the file or comma delimited list of files that should be utilized"). - Env("INPUT"). - Bind("input"). - Require() - - transformEmailCompleteBuilder.Flag().String("out", "./paypal-settlement", - "the location of the file to write out"). - Env("OUT"). - Bind("out"). - Require() - - transformBuilder.Flag().String("currency", "", - "a currency must be set (usually JPY)"). - Env("CURRENCY"). - Bind("currency"). - Require() - - completeBuilder.Flag().String("txn-id", "", - "the completed mass pay transaction id"). - Env("TXN_ID"). - Bind("txn-id"). - Require() - - transformBuilder.Flag().Float64("rate", 0, - "a currency must be set (usually JPY)"). - Bind("rate"). - Env("RATE") -} - -// PaypalEmailTemplate performs template replacement of date fields in emails -func PaypalEmailTemplate(inPath string, outPath string) (err error) { - // read in email template - data, err := ioutil.ReadFile(inPath) - if err != nil { - err = fmt.Errorf("failed to read template: %w", err) - return - } - // perform template rendering to out - f, err := os.Create(outPath) - if err != nil { - err = fmt.Errorf("failed to create output: %w", err) - return - } - defer func() { - if err = f.Close(); err != nil { - err = fmt.Errorf("failed to create output: %w", err) - return - } - }() - - var ( - today = time.Now() - // template will have a "year" and "month" field - v = struct { - Month int - Year int - }{ - Month: int(today.Month()), - Year: today.Year(), - } - t = template.Must(template.New("email").Parse(string(data))) - ) - - if err = t.Execute(f, v); err != nil { - err = fmt.Errorf("failed to execute template: %w", err) - return - } - return -} - -var ( - // PaypalSettlementCmd is the paypal command - PaypalSettlementCmd = &cobra.Command{ - Use: "paypal", - Short: "provides paypal settlement", - } - - // EmailPaypalSettlementCmd provides population of a templated email - EmailPaypalSettlementCmd = &cobra.Command{ - Use: "email", - Short: "provides population of a templated email", - Run: rootcmd.Perform("email", EmailPaypalSettlement), - } - - // CompletePaypalSettlementCmd provides completion of paypal settlement - CompletePaypalSettlementCmd = &cobra.Command{ - Use: "complete", - Short: "provides completion of paypal settlement", - Run: rootcmd.Perform("complete", CompletePaypalSettlement), - } - - // TransformPaypalSettlementCmd provides transform of paypal settlement for mass pay - TransformPaypalSettlementCmd = &cobra.Command{ - Use: "transform", - Short: "provides transform of paypal settlement for mass pay", - Run: rootcmd.Perform("transform", RunTransformPaypalSettlement), - } -) - -// EmailPaypalSettlement create the email to send to the -func EmailPaypalSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - return PaypalEmailTemplate(input, out) -} - -// RunTransformPaypalSettlement transforms a paypal settlement -func RunTransformPaypalSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - payouts, err := settlement.ReadFiles(strings.Split(input, ",")) - if err != nil { - return err - } - currency, err := cmd.Flags().GetString("currency") - if err != nil { - return err - } - rate, err := cmd.Flags().GetFloat64("rate") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - - return PaypalTransformForMassPay( - cmd.Context(), - payouts, - currency, - decimal.NewFromFloat(rate), - out, - ) -} - -// CompletePaypalSettlement added complete paypal settlement -func CompletePaypalSettlement(cmd *cobra.Command, args []string) error { - input, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - out, err := cmd.Flags().GetString("out") - if err != nil { - return err - } - txnID, err := cmd.Flags().GetString("txn-id") - if err != nil { - return err - } - - if out == "./paypal-settlement" { - // use a file with extension if none is passed - out = "./paypal-settlement-complete.json" - } - payouts, err := settlement.ReadFiles(strings.Split(input, ",")) - if err != nil { - return err - } - payouts, err = PaypalCompleteSettlement( - payouts, - txnID, - ) - if err != nil { - return err - } - err = PaypalWriteTransactions(out, payouts) - if err != nil { - return err - } - return nil -} - -// PaypalCompleteSettlement marks the settlement file as complete -func PaypalCompleteSettlement(payouts *[]custodian.Transaction, txnID string) (*[]custodian.Transaction, error) { - for i, payout := range *payouts { - if payout.WalletProvider != "paypal" { - return nil, errors.New("error, non-paypal payment included.\nThis command should be called only on the filtered paypal-settlement.json") - } - if !payout.Amount.GreaterThan(decimal.Zero) { - return nil, errors.New("error, non-zero payment included.\nThis command should be called only on the post-rate paypal-settlement.json") - } - payout.Status = "complete" - payout.ProviderID = txnID - (*payouts)[i] = payout - } - return payouts, nil -} - -// PaypalTransformArgs are the args required for the transform command -type PaypalTransformArgs struct { - In string - Currency string - Auth string - Rate decimal.Decimal - Out string -} - -// PaypalWriteTransactions writes settlement transactions to a json file -func PaypalWriteTransactions(outPath string, metadata *[]custodian.Transaction) error { - data, err := json.MarshalIndent(metadata, "", " ") - if err != nil { - return err - } - return ioutil.WriteFile(outPath, data, 0600) -} - -// PaypalWriteMassPayCSV writes a csv for using with Paypal web mass payments -func PaypalWriteMassPayCSV(ctx context.Context, outPath string, metadata *[]paypal.Metadata) error { - rows := []*paypal.MassPayRow{} - total := decimal.NewFromFloat(0) - logger := zerolog.Ctx(ctx) - currency := "" - for _, entry := range *metadata { - row := entry.ToMassPayCSVRow() - total = total.Add(row.Amount) - currency = row.Currency - rows = append(rows, row) - } - if len(rows) > 5000 { - return errors.New("a payout cannot be larger than 5000 lines items long") - } - logger.UpdateContext(func(c zerolog.Context) zerolog.Context { - return c.Int("payouts", len(rows)). - Str("total", total.String()). - Str("currency", currency) - }) - - data, err := gocsv.MarshalString(&rows) - if err != nil { - return err - } - - f, err := os.Create(outPath) - if err != nil { - return err - } - defer closers.Panic(ctx, f) - _, err = f.WriteString(data) - if err != nil { - return err - } - return nil -} - -// PaypalTransformForMassPay starts the process to transform a settlement into a mass pay csv -func PaypalTransformForMassPay(ctx context.Context, payouts *[]custodian.Transaction, currency string, rate decimal.Decimal, out string) error { - rate, err := paypal.GetRate(ctx, currency, rate) - if err != nil { - return err - } - - txs, err := paypal.CalculateTransactionAmounts(currency, rate, payouts) - if err != nil { - return err - } - - err = PaypalWriteTransactions(out+".json", txs) - if err != nil { - return err - } - - metadata, err := paypal.MergeAndTransformPayouts(txs) - if err != nil { - return err - } - - err = PaypalWriteMassPayCSV(ctx, out+".csv", metadata) - if err != nil { - return err - } - return nil -} diff --git a/tools/settlement/cmd/settlement.go b/tools/settlement/cmd/settlement.go deleted file mode 100644 index ebc7fb724..000000000 --- a/tools/settlement/cmd/settlement.go +++ /dev/null @@ -1,63 +0,0 @@ -package settlement - -import ( - "context" - "encoding/json" - "io/ioutil" - "path/filepath" - "strings" - - rootcmd "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/spf13/cobra" -) - -func init() { - rootcmd.RootCmd.AddCommand(SettlementCmd) -} - -// SettlementCmd is the settlement command -var SettlementCmd = &cobra.Command{ - Use: "settlement", - Short: "provides settlement utilities", -} - -// WriteCategorizedTransactions write out transactions categorized under a key -func WriteCategorizedTransactions( - ctx context.Context, - outPath string, - transactions map[string][]custodian.Transaction, -) error { - for key, txs := range transactions { - if len(txs) > 0 { - outputPath := strings.TrimSuffix(outPath, filepath.Ext(outPath)) + "-" + key + ".json" - err := WriteTransactions(ctx, outputPath, txs) - if err != nil { - return err - } - } - } - return nil -} - -// WriteTransactions writes settlement transactions to a json file -func WriteTransactions(ctx context.Context, outPath string, metadata []custodian.Transaction) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - - if len(metadata) == 0 { - return nil - } - - logger.Debug().Str("files", outPath).Int("num transactions", len(metadata)).Msg("writing outputting files") - data, err := json.MarshalIndent(metadata, "", " ") - if err != nil { - logger.Error().Err(err).Msg("failed writing outputting files") - return err - } - return ioutil.WriteFile(outPath, data, 0600) -} diff --git a/tools/settlement/cmd/uphold.go b/tools/settlement/cmd/uphold.go deleted file mode 100644 index be55a28d6..000000000 --- a/tools/settlement/cmd/uphold.go +++ /dev/null @@ -1,361 +0,0 @@ -package settlement - -import ( - "bufio" - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/brave-intl/bat-go/tools/settlement" - "github.com/spf13/cobra" -) - -var ( - // UpholdCmd uphold subcommand - UpholdCmd = &cobra.Command{ - Use: "uphold", - Short: "uphold sub command", - } - // UpholdUploadCmd uphold upload subcommand - UpholdUploadCmd = &cobra.Command{ - Use: "upload", - Short: "upload to uphold", - Run: rootcmd.Perform("upload", RunUpholdUpload), - } -) - -func init() { - UpholdCmd.AddCommand( - UpholdUploadCmd, - ) - - SettlementCmd.AddCommand( - UpholdCmd, - ) - - uploadBuilder := cmdutils.NewFlagBuilder(UpholdUploadCmd) - - uploadBuilder.Flag().Bool("verbose", false, - "how verbose logging should be"). - Bind("verbose") - - uploadBuilder.Flag().String("input", "", - "input file to submit to a given provider"). - Bind("input"). - Require() - - uploadBuilder.Flag().String("progress", "1s", - "how often progress should be printed out"). - Bind("progress") -} - -// RunUpholdUpload the runner that the uphold upload command calls -func RunUpholdUpload(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - verbose, err := cmd.Flags().GetBool("verbose") - if err != nil { - return err - } - progress, err := cmd.Flags().GetString("progress") - if err != nil { - return err - } - inputFile, err := cmd.Flags().GetString("input") - if err != nil { - return err - } - // setup context for logging, debug and progress - ctx = context.WithValue(ctx, appctx.DebugLoggingCTXKey, verbose) - - // setup progress logging - progressDuration, err := time.ParseDuration(progress) - if err != nil { - return err - } - progChan := logging.UpholdReportProgress(ctx, progressDuration) - ctx = context.WithValue(ctx, appctx.ProgressLoggingCTXKey, progChan) - - logFile := strings.TrimSuffix(inputFile, filepath.Ext(inputFile)) + "-log.json" - outputFilePrefix := strings.TrimSuffix(inputFile, filepath.Ext(inputFile)) - - return UpholdUpload( - ctx, - inputFile, - logFile, - outputFilePrefix, - ) -} - -func recordProgress(f *os.File, settlementTransaction *custodian.Transaction) error { - var out []byte - out, err := json.Marshal(settlementTransaction) - if err != nil { - return fmt.Errorf("failed to unmarshal settlement transaction: %v", err) - } - - // Append progress to the log - _, err = f.Write(append(out, '\n')) - if err != nil { - return fmt.Errorf("failed to write to output log: %v", err) - } - err = f.Sync() - if err != nil { - return fmt.Errorf("failed to sync output log to disk: %v", err) - } - return nil -} - -// UpholdUpload uploads transactions to uphold -func UpholdUpload( - ctx context.Context, - inputFile string, - logFile string, - outputFilePrefix string, -) error { - - // setup logger, with the context that has the logger - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - logger.Info().Msg("beginning uphold upload") - - settlementJSON, err := ioutil.ReadFile(inputFile) - if err != nil { - logger.Panic().Err(err).Msg("failed to read input file") - } - - f, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) - if err != nil { - logger.Panic().Err(err).Msg("failed to create output file") - } - - var settlementState settlement.State - err = json.Unmarshal(settlementJSON, &settlementState) - if err != nil { - logger.Panic().Err(err).Msg("failed to unmarshal input file") - } - - settlementWallet, err := uphold.FromWalletInfo(ctx, settlementState.WalletInfo) - if err != nil { - logger.Panic().Err(err).Msg("failed to make settlement wallet") - } - - // Read from the transaction log - logger.Info().Msg("scanning stateful logs to establish transaction status") - scanner := bufio.NewScanner(f) - isResubmit := false - for scanner.Scan() { - var tmp custodian.Transaction - err = json.Unmarshal(scanner.Bytes(), &tmp) - if err != nil { - logger.Panic().Err(err).Msg("failed to scan the transaction log") - } - for i := 0; i < len(settlementState.Transactions); i++ { - // Only one transaction per channel is allowed per settlement - if settlementState.Transactions[i].Channel == tmp.Channel { - isResubmit = true - settlementState.Transactions[i] = tmp - } - } - } - - // Optimize the case where we are rerunning by creating a truncated snapshot of the last state - if isResubmit { - backupFile := strings.TrimSuffix(logFile, filepath.Ext(logFile)) + "-backup.json" - backupF, err := os.OpenFile(backupFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) - if err != nil { - logger.Panic().Err(err).Msg("failed to open backup file") - } - - // Reset log offset to 0, append to the backup we just opened - _, err = f.Seek(0, 0) - if err != nil { - logger.Panic().Err(err).Msg("failed to seek log back to start") - } - nBytes, err := io.Copy(backupF, f) - if nBytes <= 0 || err != nil { - logger.Panic().Err(err).Msg("failed to backup log") - } - err = backupF.Sync() - if err != nil { - logger.Panic().Err(err).Msg("failed to sync backup log to disk") - } - - tmpFile := strings.TrimSuffix(logFile, filepath.Ext(logFile)) + "-tmp.json" - tmpF, err := os.OpenFile(tmpFile, os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - logger.Panic().Err(err).Msg("failed to create temporary file") - } - - // Snapshot the fully caught up state into a temporary file - for i := 0; i < len(settlementState.Transactions); i++ { - err = recordProgress(tmpF, &settlementState.Transactions[i]) - if err != nil { - logger.Panic().Err(err).Msg("failed to snapshot state") - } - } - - // Replace our log file handle with the temporary file handle - f = tmpF - - // Rename the temporary file, replacing the original log with the truncated snapshot - // NOTE: this is only done after we've successfully written to the new temporary file to ensure - // that even in pathological cases we always have a valid log file to resume from - err = os.Rename(tmpFile, logFile) - if err != nil { - logger.Panic().Err(err).Msg("failed to replace the log") - } - } - - var total = len(settlementState.Transactions) - - // Attempt to move all transactions into a processing state - allFinalized := true - someProcessing := false - progress := logging.UpholdProgressSet{ - Progress: []logging.UpholdProgress{{ - Message: "Successes", - Count: 0, - }}, - } - for i := 0; i < total; i++ { - settlementTransaction := &settlementState.Transactions[i] - - if settlementTransaction.IsComplete() || settlementTransaction.IsFailed() { - continue - } - - err = settlement.SubmitPreparedTransaction(ctx, settlementWallet, settlementTransaction) - if err != nil { - logger.Error().Err(err).Msg("unanticipated error") - settlementTransaction.FailureReason = fmt.Sprintf("unanticipated error: %e", err) - allFinalized = false - continue - } - - err = recordProgress(f, settlementTransaction) - if err != nil { - logger.Panic().Err(err).Msg("failed to record progress") - } - - err = settlement.ConfirmPreparedTransaction(ctx, settlementWallet, settlementTransaction, isResubmit) - if err != nil { - logger.Panic().Err(err).Msg("failed to confirm prepared transaction") - } - - err = recordProgress(f, settlementTransaction) - if err != nil { - logger.Panic().Err(err).Msg("failed to record progress") - } - - // We will later attempt to resolve all processing to complete or failed - if settlementTransaction.IsProcessing() { - someProcessing = true - } else if !settlementTransaction.IsComplete() && !settlementTransaction.IsFailed() { - allFinalized = false - } - - // Progress is tracked on an error by error basis based on a string - // comparison of errors. Each iteration we need to see if the error - // message we received matches any messages we have already received. If - // yes, increment the count. If no, add this new error with count 1. If - // there was no error, increment or create a success progress entry. - for p := 0; p < len(progress.Progress); p++ { - existingProgressEntry := progress.Progress[p] - progressMessage := settlementTransaction.FailureReason - if settlementTransaction.FailureReason == "" { - progressMessage = "Successes" - } - - if existingProgressEntry.Message == progressMessage { - progress.Progress[p].Count++ - break - } else if p == len(progress.Progress)-1 { - progress.Progress = append(progress.Progress, logging.UpholdProgress{ - Message: progressMessage, - Count: 1, - }) - } - } - - // perform progress logging - logging.UpholdSubmitProgress(ctx, progress) - } - - // While there are transactions in the processing state, attempt to resolve them to complete or failed - for someProcessing { - someProcessing = false - for i := 0; i < total; i++ { - settlementTransaction := &settlementState.Transactions[i] - - if settlementTransaction.IsProcessing() { - logger.Info().Msg("reattempting to confirm transaction in progress") - // Confirm will first check if the transaction has already been confirmed - err = settlement.ConfirmPreparedTransaction(ctx, settlementWallet, settlementTransaction, true) - if err != nil { - logger.Panic().Err(err).Msg("failed to confirm prepared transaction") - } - - err = recordProgress(f, settlementTransaction) - if err != nil { - logger.Panic().Err(err).Msg("failed to record progress") - } - - if settlementTransaction.IsProcessing() { - someProcessing = true - } - } - } - } - - if allFinalized { - logger.Info().Msg("all transactions finalized, writing out settlement file") - } else { - logger.Error().Msg("not all transactions are finalized, rerun to resubmit") - return nil - } - - transactionsMap := make(map[string][]custodian.Transaction) - for i := 0; i < len(settlementState.Transactions); i++ { - logger.Info().Msg("redacting transactions in log files") - // Redact signed transactions - settlementState.Transactions[i].SignedTx = "" - - // Group by status - logger.Info().Msg("grouping transactions by status") - status := settlementState.Transactions[i].Status - transactionsMap[status] = append(transactionsMap[status], settlementState.Transactions[i]) - } - - for key, txs := range transactionsMap { - outputFile := outputFilePrefix + "-" + key + ".json" - logger.Info().Msg(fmt.Sprintf("writing out transactions to %s for eyeshade", outputFile)) - - // Write out transactions ready to be submitted to eyeshade - out, err := json.MarshalIndent(txs, "", " ") - if err != nil { - logger.Panic().Err(err).Msg("failed to marshal settlement transactions to eyeshade input") - } - - err = ioutil.WriteFile(outputFile, out, 0600) - if err != nil { - logger.Panic().Err(err).Msg("failed to write out settlement transactions to eyeshade input") - } - } - - logger.Info().Msg("done!") - return nil -} diff --git a/tools/settlement/config.example.yaml b/tools/settlement/config.example.yaml deleted file mode 100644 index a5ffba347..000000000 --- a/tools/settlement/config.example.yaml +++ /dev/null @@ -1,6 +0,0 @@ -wallets: - uphold-contribution: 'uphold-contribution' - gemini-contribution: 'gemini-contribution' - uphold-referral: 'uphold-referral' - gemini-referral: 'gemini-referral' - bitflyer-default: 'bitflyer-default' diff --git a/tools/settlement/config.go b/tools/settlement/config.go deleted file mode 100644 index b57a8053c..000000000 --- a/tools/settlement/config.go +++ /dev/null @@ -1,50 +0,0 @@ -package settlement - -import ( - "os" - "os/user" - "path" - - "gopkg.in/yaml.v2" -) - -// Config a space for complex inputs -type Config struct { - Wallets map[string]string `yaml:"wallets"` -} - -// GetWalletKey accesses the wallet config -func (config *Config) GetWalletKey(key string) string { - value := config.Wallets[key] - if value == "" { - return key - } - return value -} - -// ReadYamlConfig reads a yaml config -func ReadYamlConfig(configPath string) (*Config, error) { - if configPath == "" { - usr, err := user.Current() - if err != nil { - return nil, err - } - configPath = path.Join(usr.HomeDir, ".settlement.yaml") - } - // Open config file - var config Config - file, err := os.Open(configPath) - if err != nil { - return nil, err - } - defer func() { _ = file.Close() }() - - // Init new YAML decode - d := yaml.NewDecoder(file) - - // Start YAML decoding from file - if err := d.Decode(&config); err != nil { - return nil, err - } - return &config, nil -} diff --git a/tools/settlement/config.hcl b/tools/settlement/config.hcl deleted file mode 100644 index 3b8d29b2e..000000000 --- a/tools/settlement/config.hcl +++ /dev/null @@ -1,8 +0,0 @@ -storage "file" { - path = "./vault-data" -} - -listener "tcp" { - address = "127.0.0.1:8200" - tls_disable = 1 -} diff --git a/tools/settlement/files.go b/tools/settlement/files.go deleted file mode 100644 index 9cec5d409..000000000 --- a/tools/settlement/files.go +++ /dev/null @@ -1,26 +0,0 @@ -package settlement - -import ( - "encoding/json" - "io/ioutil" - - "github.com/brave-intl/bat-go/libs/custodian" -) - -// ReadFiles reads a series of files -func ReadFiles(filesPaths []string) (*[]custodian.Transaction, error) { - var allPayouts []custodian.Transaction - for _, filePath := range filesPaths { - var transactionList []custodian.Transaction - bytes, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, err - } - err = json.Unmarshal(bytes, &transactionList) - if err != nil { - return nil, err - } - allPayouts = append(allPayouts, transactionList...) - } - return &allPayouts, nil -} diff --git a/tools/settlement/gemini/sign.go b/tools/settlement/gemini/sign.go deleted file mode 100644 index 50f70c67d..000000000 --- a/tools/settlement/gemini/sign.go +++ /dev/null @@ -1,62 +0,0 @@ -package geminisettlement - -import ( - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - - "github.com/brave-intl/bat-go/libs/clients/gemini" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" -) - -// SignRequests signs formed requests -func SignRequests( - clientID string, - clientKey string, - hmacSecret *vaultsigner.HmacSigner, - privateRequests *[][]gemini.PayoutPayload, -) (*[]gemini.PrivateRequestSequence, error) { - privateRequestSequences := make([]gemini.PrivateRequestSequence, 0) - // sign each request - - if len(clientID) == 0 { - return nil, errors.New("a client id was missing during the gemini settlement signing process") - } - - for i := range *privateRequests { - privateRequestRequirements := (*privateRequests)[i] - base := gemini.NewBulkPayoutPayload( - nil, - clientID, - &privateRequestRequirements, - ) - signatures := []string{} - // store the original nonce - originalNonce := base.Nonce - for i := 0; i < 10; i++ { - // increment the nonce to correspond to each signature - base.Nonce = originalNonce + int64(i) - marshalled, err := json.Marshal(base) - if err != nil { - return nil, err - } - serializedPayload := base64.StdEncoding.EncodeToString(marshalled) - sig, err := hmacSecret.HMACSha384( - []byte(serializedPayload), - ) - if err != nil { - return nil, err - } - signatures = append(signatures, hex.EncodeToString(sig)) - } - base.Nonce = originalNonce - requestSequence := gemini.PrivateRequestSequence{ - Signatures: signatures, - Base: base, - APIKey: clientKey, - } - privateRequestSequences = append(privateRequestSequences, requestSequence) - } - return &privateRequestSequences, nil -} diff --git a/tools/settlement/gemini/upload.go b/tools/settlement/gemini/upload.go deleted file mode 100644 index fa71e2b1b..000000000 --- a/tools/settlement/gemini/upload.go +++ /dev/null @@ -1,346 +0,0 @@ -package geminisettlement - -import ( - "context" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "io/ioutil" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients/gemini" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/cryptography" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/rs/zerolog" - uuid "github.com/satori/go.uuid" - "github.com/shopspring/decimal" -) - -// CategorizeResponse categorizes a response from gemini as pending, complete, failed, or unknown -func CategorizeResponse( - originalTransactions map[string]custodian.Transaction, - payout *gemini.PayoutResult, -) (custodian.Transaction, string) { - original := originalTransactions[payout.TxRef] - key := "failed" - if payout.Result == "Error" { - original.Note = *payout.Reason - } else { - status := *payout.Status - key = "unknown" - if *payout.Status == "Pending" { - key = "pending" - } else if status == "Completed" { - key = "complete" - } - } - original.Status = key - tmp := altcurrency.BAT - original.AltCurrency = &tmp - original.Currency = tmp.String() - original.ProviderID = payout.TxRef - return original, key -} - -// CategorizeResponses categorizes the series of responses -func CategorizeResponses( - originalTransactions map[string]custodian.Transaction, - response *[]gemini.PayoutResult, -) map[string][]custodian.Transaction { - transactions := make(map[string][]custodian.Transaction) - - for _, payout := range *response { - original, key := CategorizeResponse( - originalTransactions, - &payout, - ) - transactions[key] = append(transactions[key], original) - } - return transactions -} - -// SubmitBulkPayoutTransactions submits bulk payout transactions -func SubmitBulkPayoutTransactions( - ctx context.Context, - transactionsMap map[string]custodian.Transaction, - submittedTransactions map[string][]custodian.Transaction, - bulkPayoutRequestRequirements gemini.PrivateRequestSequence, - geminiClient gemini.Client, - total int, - blockProgress int, - signatureSwitch int, -) (map[string][]custodian.Transaction, error) { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - logging.SubmitProgress(ctx, blockProgress, total) - // make sure payload is parsable - // upload the bulk payout - sig := bulkPayoutRequestRequirements.Signatures[signatureSwitch] - decodedSig, err := hex.DecodeString(sig) - if err != nil { - logger.Error().Err(err).Msg("failed decode signature") - return submittedTransactions, err - } - base := bulkPayoutRequestRequirements.Base - presigner := cryptography.NewPresigner(decodedSig) - base.Nonce = base.Nonce + int64(signatureSwitch) - - logger.Debug(). - Int("total", total). - Int("progress", blockProgress). - Int64("nonce", base.Nonce). - Int("signature switch", signatureSwitch). - Msg("parameters used") - - serialized, err := json.Marshal(base) - if err != nil { - return submittedTransactions, err - } - payload := base64.StdEncoding.EncodeToString(serialized) - - logger.Debug(). - Str("api_key", bulkPayoutRequestRequirements.APIKey). - Str("signature", sig). - Msg("sending request") - - response, err := geminiClient.UploadBulkPayout( - ctx, - bulkPayoutRequestRequirements.APIKey, - presigner, - payload, - ) - <-time.After(time.Second) - if err != nil { - logger.Error().Err(err).Msg("error performing upload") - return submittedTransactions, err - } - // collect all successful transactions to send to eyeshade - submitted := CategorizeResponses(transactionsMap, response) - for key, txs := range submitted { - submittedTransactions[key] = append(submittedTransactions[key], txs...) - } - return submittedTransactions, nil -} - -// CheckPayoutTransactionsStatus checks the status of given transactions -func CheckPayoutTransactionsStatus( - ctx context.Context, - transactionsMap map[string]custodian.Transaction, - submittedTransactions map[string][]custodian.Transaction, - bulkPayoutRequestRequirements gemini.PrivateRequestSequence, - geminiClient gemini.Client, - total int, - blockProgress int, -) (map[string][]custodian.Transaction, error) { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - - APIKey := bulkPayoutRequestRequirements.APIKey - base := bulkPayoutRequestRequirements.Base - clientID := base.OauthClientID - for j, payout := range base.Payouts { - result, err := geminiClient.CheckTxStatus( - ctx, - APIKey, - clientID, - payout.TxRef, - ) - if err != nil { - return nil, err - } - original, key := CategorizeResponse( - transactionsMap, - result, - ) - submittedTransactions[key] = append(submittedTransactions[key], original) - - prog := blockProgress + j - logging.SubmitProgress(ctx, prog, total) - logger.Debug(). - Int("total", total). - Int("progress", prog). - Str("key", key). - Str("tx_ref", payout.TxRef). - Msg("parameters used") - } - return submittedTransactions, err -} - -// ConvertTransactionsToPayouts converts transactions from antifraud to "payouts" for gemini -func ConvertTransactionsToPayouts(transactions *[]custodian.Transaction, txID string) (*[]gemini.PayoutPayload, decimal.Decimal) { - payouts := make([]gemini.PayoutPayload, 0) - total := decimal.NewFromFloat(0) - for i, tx := range *transactions { - tx.SettlementID = txID - (*transactions)[i] = tx - payout := gemini.SettlementTransactionToPayoutPayload(&tx) - total = total.Add(payout.Amount) - payouts = append(payouts, payout) - } - return &payouts, total -} - -// TransformTransactions splits the transactions into appropriately sized blocks for signing -func TransformTransactions(ctx context.Context, oauthClientID string, transactions []custodian.Transaction) (*[][]gemini.PayoutPayload, error) { - maxCount := 30 - blocksCount := (len(transactions) / maxCount) + 1 - privateRequests := make([][]gemini.PayoutPayload, 0) - i := 0 - logger := zerolog.Ctx(ctx) - - txnID := transactions[0].SettlementID - txID := uuid.Must(uuid.FromString(txnID)) - total := decimal.NewFromFloat(0) - for i < blocksCount { - lowerBound := i * maxCount - upperBound := (i + 1) * maxCount - payoutLength := len(transactions) - if payoutLength <= upperBound { - upperBound = payoutLength - } - transactionBlock := transactions[lowerBound:upperBound] - if len(transactionBlock) > 0 { - payoutBlock, blockTotal := ConvertTransactionsToPayouts(&transactionBlock, txnID) - total = total.Add(blockTotal) - privateRequests = append(privateRequests, *payoutBlock) - } - i++ - } - - logger.UpdateContext(func(c zerolog.Context) zerolog.Context { - return c.Str("transaction_id", txID.String()). - Int("blocks", blocksCount). - Int("transactions", len(transactions)). - Str("total", total.String()) - }) - - return &privateRequests, nil -} - -// IterateRequest iterates requests -func IterateRequest( - ctx context.Context, - action string, - geminiClient gemini.Client, - signatureSwitch int, - bulkPayoutFiles []string, - transactionsMap map[string]custodian.Transaction, -) (map[string][]custodian.Transaction, error) { - - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - - submittedTransactions := make(map[string][]custodian.Transaction) - - apiSecret, err := appctx.GetStringFromContext(ctx, appctx.GeminiAPISecretCTXKey) - if err != nil { - logger.Error().Err(err).Msg("failed to get gemini api secret") - return submittedTransactions, fmt.Errorf("failed to get gemini api secret: %w", err) - } - apiKey, err := appctx.GetStringFromContext(ctx, appctx.GeminiAPIKeyCTXKey) - if err != nil { - logger.Error().Err(err).Msg("failed to get gemini api key") - return submittedTransactions, fmt.Errorf("failed to get gemini api key: %w", err) - } - - for _, bulkPayoutFile := range bulkPayoutFiles { - bytes, err := ioutil.ReadFile(bulkPayoutFile) - if err != nil { - logger.Error().Err(err).Msg("failed to read bulk payout file") - return submittedTransactions, err - } - - var geminiBulkPayoutRequestRequirements []gemini.PrivateRequestSequence - err = json.Unmarshal(bytes, &geminiBulkPayoutRequestRequirements) - if err != nil { - logger.Error().Err(err).Msg("failed unmarshal bulk payout file") - return submittedTransactions, err - } - - total := geminiComputeTotal(geminiBulkPayoutRequestRequirements) - for i, bulkPayoutRequestRequirements := range geminiBulkPayoutRequestRequirements { - blockProgress := geminiComputeTotal(geminiBulkPayoutRequestRequirements[:i+1]) - if action == "upload" { - payload, err := json.Marshal(gemini.NewBalancesPayload(nil)) - if err != nil { - logger.Error().Err(err).Msg("failed unmarshal balance payload") - return submittedTransactions, err - } - - signer := cryptography.NewHMACHasher([]byte(apiSecret)) - result, err := geminiClient.FetchBalances(ctx, apiKey, signer, string(payload)) - availableCurrency := map[string]decimal.Decimal{} - for _, currency := range *result { - availableCurrency[currency.Currency] = currency.Amount - } - - requiredCurrency := map[string]decimal.Decimal{} - for _, pay := range bulkPayoutRequestRequirements.Base.Payouts { - requiredCurrency[pay.Currency] = requiredCurrency[pay.Currency].Add(pay.Amount) - } - - for key, amount := range requiredCurrency { - if availableCurrency[key].LessThan(amount) { - logger.Error().Str("required", amount.String()).Str("available", availableCurrency[key].String()).Str("currency", key).Err(err).Msg("failed to meet required balance") - return submittedTransactions, fmt.Errorf("failed to meet required balance: %w", err) - } - } - - submittedTransactions, err = SubmitBulkPayoutTransactions( - ctx, - transactionsMap, - submittedTransactions, - bulkPayoutRequestRequirements, - geminiClient, - len(bulkPayoutFiles), - blockProgress, - signatureSwitch, - ) - if err != nil { - logger.Error().Err(err).Msg("failed to submit bulk payout transactions") - return nil, err - } - } else if action == "checkstatus" { - submittedTransactions, err = CheckPayoutTransactionsStatus( - ctx, - transactionsMap, - submittedTransactions, - bulkPayoutRequestRequirements, - geminiClient, - total, - blockProgress, - ) - if err != nil { - logger.Error().Err(err).Msg("failed to check payout transactions status") - return nil, err - } - } - } - } - return submittedTransactions, nil -} - -func geminiComputeTotal(geminiBulkPayoutRequestRequirements []gemini.PrivateRequestSequence) int { - if len(geminiBulkPayoutRequestRequirements) == 0 { - return 0 - } - - firstLen := len(geminiBulkPayoutRequestRequirements[0].Base.Payouts) - blockLen := len(geminiBulkPayoutRequestRequirements) - lastLen := len(geminiBulkPayoutRequestRequirements[blockLen-1].Base.Payouts) - total := blockLen * firstLen - if blockLen > 1 { - total += lastLen - } - return total -} diff --git a/tools/settlement/hashicorp.asc b/tools/settlement/hashicorp.asc deleted file mode 100644 index 4071d3516..000000000 --- a/tools/settlement/hashicorp.asc +++ /dev/null @@ -1,122 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBGB9+xkBEACabYZOWKmgZsHTdRDiyPJxhbuUiKX65GUWkyRMJKi/1dviVxOX -PG6hBPtF48IFnVgxKpIb7G6NjBousAV+CuLlv5yqFKpOZEGC6sBV+Gx8Vu1CICpl -Zm+HpQPcIzwBpN+Ar4l/exCG/f/MZq/oxGgH+TyRF3XcYDjG8dbJCpHO5nQ5Cy9h -QIp3/Bh09kET6lk+4QlofNgHKVT2epV8iK1cXlbQe2tZtfCUtxk+pxvU0UHXp+AB -0xc3/gIhjZp/dePmCOyQyGPJbp5bpO4UeAJ6frqhexmNlaw9Z897ltZmRLGq1p4a -RnWL8FPkBz9SCSKXS8uNyV5oMNVn4G1obCkc106iWuKBTibffYQzq5TG8FYVJKrh -RwWB6piacEB8hl20IIWSxIM3J9tT7CPSnk5RYYCTRHgA5OOrqZhC7JefudrP8n+M -pxkDgNORDu7GCfAuisrf7dXYjLsxG4tu22DBJJC0c/IpRpXDnOuJN1Q5e/3VUKKW -mypNumuQpP5lc1ZFG64TRzb1HR6oIdHfbrVQfdiQXpvdcFx+Fl57WuUraXRV6qfb -4ZmKHX1JEwM/7tu21QE4F1dz0jroLSricZxfaCTHHWNfvGJoZ30/MZUrpSC0IfB3 -iQutxbZrwIlTBt+fGLtm3vDtwMFNWM+Rb1lrOxEQd2eijdxhvBOHtlIcswARAQAB -tERIYXNoaUNvcnAgU2VjdXJpdHkgKGhhc2hpY29ycC5jb20vc2VjdXJpdHkpIDxz -ZWN1cml0eUBoYXNoaWNvcnAuY29tPokCVAQTAQoAPhYhBMh0AR8KtAURDQIQVTQ2 -XZRy10aPBQJgffsZAhsDBQkJZgGABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ -EDQ2XZRy10aPtpcP/0PhJKiHtC1zREpRTrjGizoyk4Sl2SXpBZYhkdrG++abo6zs -buaAG7kgWWChVXBo5E20L7dbstFK7OjVs7vAg/OLgO9dPD8n2M19rpqSbbvKYWvp -0NSgvFTT7lbyDhtPj0/bzpkZEhmvQaDWGBsbDdb2dBHGitCXhGMpdP0BuuPWEix+ -QnUMaPwU51q9GM2guL45Tgks9EKNnpDR6ZdCeWcqo1IDmklloidxT8aKL21UOb8t -cD+Bg8iPaAr73bW7Jh8TdcV6s6DBFub+xPJEB/0bVPmq3ZHs5B4NItroZ3r+h3ke -VDoSOSIZLl6JtVooOJ2la9ZuMqxchO3mrXLlXxVCo6cGcSuOmOdQSz4OhQE5zBxx -LuzA5ASIjASSeNZaRnffLIHmht17BPslgNPtm6ufyOk02P5XXwa69UCjA3RYrA2P -QNNC+OWZ8qQLnzGldqE4MnRNAxRxV6cFNzv14ooKf7+k686LdZrP/3fQu2p3k5rY -0xQUXKh1uwMUMtGR867ZBYaxYvwqDrg9XB7xi3N6aNyNQ+r7zI2lt65lzwG1v9hg -FG2AHrDlBkQi/t3wiTS3JOo/GCT8BjN0nJh0lGaRFtQv2cXOQGVRW8+V/9IpqEJ1 -qQreftdBFWxvH7VJq2mSOXUJyRsoUrjkUuIivaA9Ocdipk2CkP8bpuGz7ZF4uQIN -BGB9+xkBEACoklYsfvWRCjOwS8TOKBTfl8myuP9V9uBNbyHufzNETbhYeT33Cj0M -GCNd9GdoaknzBQLbQVSQogA+spqVvQPz1MND18GIdtmr0BXENiZE7SRvu76jNqLp -KxYALoK2Pc3yK0JGD30HcIIgx+lOofrVPA2dfVPTj1wXvm0rbSGA4Wd4Ng3d2AoR -G/wZDAQ7sdZi1A9hhfugTFZwfqR3XAYCk+PUeoFrkJ0O7wngaon+6x2GJVedVPOs -2x/XOR4l9ytFP3o+5ILhVnsK+ESVD9AQz2fhDEU6RhvzaqtHe+sQccR3oVLoGcat -ma5rbfzH0Fhj0JtkbP7WreQf9udYgXxVJKXLQFQgel34egEGG+NlbGSPG+qHOZtY -4uWdlDSvmo+1P95P4VG/EBteqyBbDDGDGiMs6lAMg2cULrwOsbxWjsWka8y2IN3z -1stlIJFvW2kggU+bKnQ+sNQnclq3wzCJjeDBfucR3a5WRojDtGoJP6Fc3luUtS7V -5TAdOx4dhaMFU9+01OoH8ZdTRiHZ1K7RFeAIslSyd4iA/xkhOhHq89F4ECQf3Bt4 -ZhGsXDTaA/VgHmf3AULbrC94O7HNqOvTWzwGiWHLfcxXQsr+ijIEQvh6rHKmJK8R -9NMHqc3L18eMO6bqrzEHW0Xoiu9W8Yj+WuB3IKdhclT3w0pO4Pj8gQARAQABiQI8 -BBgBCgAmFiEEyHQBHwq0BRENAhBVNDZdlHLXRo8FAmB9+xkCGwwFCQlmAYAACgkQ -NDZdlHLXRo9ZnA/7BmdpQLeTjEiXEJyW46efxlV1f6THn9U50GWcE9tebxCXgmQf -u+Uju4hreltx6GDi/zbVVV3HCa0yaJ4JVvA4LBULJVe3ym6tXXSYaOfMdkiK6P1v -JgfpBQ/b/mWB0yuWTUtWx18BQQwlNEQWcGe8n1lBbYsH9g7QkacRNb8tKUrUbWlQ -QsU8wuFgly22m+Va1nO2N5C/eE/ZEHyN15jEQ+QwgQgPrK2wThcOMyNMQX/VNEr1 -Y3bI2wHfZFjotmek3d7ZfP2VjyDudnmCPQ5xjezWpKbN1kvjO3as2yhcVKfnvQI5 -P5Frj19NgMIGAp7X6pF5Csr4FX/Vw316+AFJd9Ibhfud79HAylvFydpcYbvZpScl -7zgtgaXMCVtthe3GsG4gO7IdxxEBZ/Fm4NLnmbzCIWOsPMx/FxH06a539xFq/1E2 -1nYFjiKg8a5JFmYU/4mV9MQs4bP/3ip9byi10V+fEIfp5cEEmfNeVeW5E7J8PqG9 -t4rLJ8FR4yJgQUa2gs2SNYsjWQuwS/MJvAv4fDKlkQjQmYRAOp1SszAnyaplvri4 -ncmfDsf0r65/sd6S40g5lHH8LIbGxcOIN6kwthSTPWX89r42CbY8GzjTkaeejNKx -v1aCrO58wAtursO1DiXCvBY7+NdafMRnoHwBk50iPqrVkNA8fv+auRyB2/G5Ag0E -YH3+JQEQALivllTjMolxUW2OxrXb+a2Pt6vjCBsiJzrUj0Pa63U+lT9jldbCCfgP -wDpcDuO1O05Q8k1MoYZ6HddjWnqKG7S3eqkV5c3ct3amAXp513QDKZUfIDylOmhU -qvxjEgvGjdRjz6kECFGYr6Vnj/p6AwWv4/FBRFlrq7cnQgPynbIH4hrWvewp3Tqw -GVgqm5RRofuAugi8iZQVlAiQZJo88yaztAQ/7VsXBiHTn61ugQ8bKdAsr8w/ZZU5 -HScHLqRolcYg0cKN91c0EbJq9k1LUC//CakPB9mhi5+aUVUGusIM8ECShUEgSTCi -KQiJUPZ2CFbbPE9L5o9xoPCxjXoX+r7L/WyoCPTeoS3YRUMEnWKvc42Yxz3meRb+ -BmaqgbheNmzOah5nMwPupJYmHrjWPkX7oyyHxLSFw4dtoP2j6Z7GdRXKa2dUYdk2 -x3JYKocrDoPHh3Q0TAZujtpdjFi1BS8pbxYFb3hHmGSdvz7T7KcqP7ChC7k2RAKO -GiG7QQe4NX3sSMgweYpl4OwvQOn73t5CVWYp/gIBNZGsU3Pto8g27vHeWyH9mKr4 -cSepDhw+/X8FGRNdxNfpLKm7Vc0Sm9Sof8TRFrBTqX+vIQupYHRi5QQCuYaV6OVr -ITeegNK3So4m39d6ajCR9QxRbmjnx9UcnSYYDmIB6fpBuwT0ogNtABEBAAGJBHIE -GAEKACYCGwIWIQTIdAEfCrQFEQ0CEFU0Nl2UctdGjwUCYH4bgAUJAeFQ2wJAwXQg -BBkBCgAdFiEEs2y6kaLAcwxDX8KAsLRBCXaFtnYFAmB9/iUACgkQsLRBCXaFtnYX -BhAAlxejyFXoQwyGo9U+2g9N6LUb/tNtH29RHYxy4A3/ZUY7d/FMkArmh4+dfjf0 -p9MJz98Zkps20kaYP+2YzYmaizO6OA6RIddcEXQDRCPHmLts3097mJ/skx9qLAf6 -rh9J7jWeSqWO6VW6Mlx8j9m7sm3Ae1OsjOx/m7lGZOhY4UYfY627+Jf7WQ5103Qs -lgQ09es/vhTCx0g34SYEmMW15Tc3eCjQ21b1MeJD/V26npeakV8iCZ1kHZHawPq/ -aCCuYEcCeQOOteTWvl7HXaHMhHIx7jjOd8XX9V+UxsGz2WCIxX/j7EEEc7CAxwAN -nWp9jXeLfxYfjrUB7XQZsGCd4EHHzUyCf7iRJL7OJ3tz5Z+rOlNjSgci+ycHEccL -YeFAEV+Fz+sj7q4cFAferkr7imY1XEI0Ji5P8p/uRYw/n8uUf7LrLw5TzHmZsTSC -UaiL4llRzkDC6cVhYfqQWUXDd/r385OkE4oalNNE+n+txNRx92rpvXWZ5qFYfv7E -95fltvpXc0iOugPMzyof3lwo3Xi4WZKc1CC/jEviKTQhfn3WZukuF5lbz3V1PQfI -xFsYe9WYQmp25XGgezjXzp89C/OIcYsVB1KJAKihgbYdHyUN4fRCmOszmOUwEAKR -3k5j4X8V5bk08sA69NVXPn2ofxyk3YYOMYWW8ouObnXoS8QJEDQ2XZRy10aPMpsQ -AIbwX21erVqUDMPn1uONP6o4NBEq4MwG7d+fT85rc1U0RfeKBwjucAE/iStZDQoM -ZKWvGhFR+uoyg1LrXNKuSPB82unh2bpvj4zEnJsJadiwtShTKDsikhrfFEK3aCK8 -Zuhpiu3jxMFDhpFzlxsSwaCcGJqcdwGhWUx0ZAVD2X71UCFoOXPjF9fNnpy80YNp -flPjj2RnOZbJyBIM0sWIVMd8F44qkTASf8K5Qb47WFN5tSpePq7OCm7s8u+lYZGK -wR18K7VliundR+5a8XAOyUXOL5UsDaQCK4Lj4lRaeFXunXl3DJ4E+7BKzZhReJL6 -EugV5eaGonA52TWtFdB8p+79wPUeI3KcdPmQ9Ll5Zi/jBemY4bzasmgKzNeMtwWP -fk6WgrvBwptqohw71HDymGxFUnUP7XYYjic2sVKhv9AevMGycVgwWBiWroDCQ9Ja -btKfxHhI2p+g+rcywmBobWJbZsujTNjhtme+kNn1mhJsD3bKPjKQfAxaTskBLb0V -wgV21891TS1Dq9kdPLwoS4XNpYg2LLB4p9hmeG3fu9+OmqwY5oKXsHiWc43dei9Y -yxZ1AAUOIaIdPkq+YG/PhlGE4YcQZ4RPpltAr0HfGgZhmXWigbGS+66pUj+Ojysc -j0K5tCVxVu0fhhFpOlHv0LWaxCbnkgkQH9jfMEJkAWMOuQINBGCAXCYBEADW6RNr -ZVGNXvHVBqSiOWaxl1XOiEoiHPt50Aijt25yXbG+0kHIFSoR+1g6Lh20JTCChgfQ -kGGjzQvEuG1HTw07YhsvLc0pkjNMfu6gJqFox/ogc53mz69OxXauzUQ/TZ27GDVp -UBu+EhDKt1s3OtA6Bjz/csop/Um7gT0+ivHyvJ/jGdnPEZv8tNuSE/Uo+hn/Q9hg -8SbveZzo3C+U4KcabCESEFl8Gq6aRi9vAfa65oxD5jKaIz7cy+pwb0lizqlW7H9t -Qlr3dBfdIcdzgR55hTFC5/XrcwJ6/nHVH/xGskEasnfCQX8RYKMuy0UADJy72TkZ -bYaCx+XXIcVB8GTOmJVoAhrTSSVLAZspfCnjwnSxisDn3ZzsYrq3cV6sU8b+QlIX -7VAjurE+5cZiVlaxgCjyhKqlGgmonnReWOBacCgL/UvuwMmMp5TTLmiLXLT7uxeG -ojEyoCk4sMrqrU1jevHyGlDJH9Taux15GILDwnYFfAvPF9WCid4UZ4Ouwjcaxfys -3LxNiZIlUsXNKwS3mhiMRL4TRsbs4k4QE+LIMOsauIvcvm8/frydvQ/kUwIhVTH8 -0XGOH909bYtJvY3fudK7ShIwm7ZFTduBJUG473E/Fn3VkhTmBX6+PjOC50HR/Hyb -waRCzfDruMe3TAcE/tSP5CUOb9C7+P+hPzQcDwARAQABiQRyBBgBCgAmFiEEyHQB -Hwq0BRENAhBVNDZdlHLXRo8FAmCAXCYCGwIFCQlmAYACQAkQNDZdlHLXRo/BdCAE -GQEKAB0WIQQ3TsdbSFkTYEqDHMfIIMbVzSerhwUCYIBcJgAKCRDIIMbVzSerh0Xw -D/9ghnUsoNCu1OulcoJdHboMazJvDt/znttdQSnULBVElgM5zk0Uyv87zFBzuCyQ -JWL3bWesQ2uFx5fRWEPDEfWVdDrjpQGb1OCCQyz1QlNPV/1M1/xhKGS9EeXrL8Dw -F6KTGkRwn1yXiP4BGgfeFIQHmJcKXEZ9HkrpNb8mcexkROv4aIPAwn+IaE+NHVtt -IBnufMXLyfpkWJQtJa9elh9PMLlHHnuvnYLvuAoOkhuvs7fXDMpfFZ01C+QSv1dz -Hm52GSStERQzZ51w4c0rYDneYDniC/sQT1x3dP5Xf6wzO+EhRMabkvoTbMqPsTEP -xyWr2pNtTBYp7pfQjsHxhJpQF0xjGN9C39z7f3gJG8IJhnPeulUqEZjhRFyVZQ6/ -siUeq7vu4+dM/JQL+i7KKe7Lp9UMrG6NLMH+ltaoD3+lVm8fdTUxS5MNPoA/I8cK -1OWTJHkrp7V/XaY7mUtvQn5V1yET5b4bogz4nME6WLiFMd+7x73gB+YJ6MGYNuO8 -e/NFK67MfHbk1/AiPTAJ6s5uHRQIkZcBPG7y5PpfcHpIlwPYCDGYlTajZXblyKrw -BttVnYKvKsnlysv11glSg0DphGxQJbXzWpvBNyhMNH5dffcfvd3eXJAxnD81GD2z -ZAriMJ4Av2TfeqQ2nxd2ddn0jX4WVHtAvLXfCgLM2Gveho4jD/9sZ6PZz/rEeTvt -h88t50qPcBa4bb25X0B5FO3TeK2LL3VKLuEp5lgdcHVonrcdqZFobN1CgGJua8TW -SprIkh+8ATZ/FXQTi01NzLhHXT1IQzSpFaZw0gb2f5ruXwvTPpfXzQrs2omY+7s7 -fkCwGPesvpSXPKn9v8uhUwD7NGW/Dm+jUM+QtC/FqzX7+/Q+OuEPjClUh1cqopCZ -EvAI3HjnavGrYuU6DgQdjyGT/UDbuwbCXqHxHojVVkISGzCTGpmBcQYQqhcFRedJ -yJlu6PSXlA7+8Ajh52oiMJ3ez4xSssFgUQAyOB16432tm4erpGmCyakkoRmMUn3p -wx+QIppxRlsHznhcCQKR3tcblUqH3vq5i4/ZAihusMCa0YrShtxfdSb13oKX+pFr -aZXvxyZlCa5qoQQBV1sowmPL1N2j3dR9TVpdTyCFQSv4KeiExmowtLIjeCppRBEK -eeYHJnlfkyKXPhxTVVO6H+dU4nVu0ASQZ07KiQjbI+zTpPKFLPp3/0sPRJM57r1+ -aTS71iR7nZNZ1f8LZV2OvGE6fJVtgJ1J4Nu02K54uuIhU3tg1+7Xt+IqwRc9rbVr -pHH/hFCYBPW2D2dxB+k2pQlg5NI+TpsXj5Zun8kRw5RtVb+dLuiH/xmxArIee8Jq -ZF5q4h4I33PSGDdSvGXn9UMY5Isjpg== -=7pIB ------END PGP PUBLIC KEY BLOCK----- diff --git a/tools/settlement/paypal/data.go b/tools/settlement/paypal/data.go deleted file mode 100644 index 7cf405953..000000000 --- a/tools/settlement/paypal/data.go +++ /dev/null @@ -1,94 +0,0 @@ -package paypal - -import ( - "crypto/sha256" - "errors" - "fmt" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/shengdoushi/base58" - "github.com/shopspring/decimal" -) - -var ( - supportedCurrencies = map[string]float64{ - "JPY": 0, - } - currencySymbols = map[string]string{ - "JPY": "¥", - } -) - -// GenerateRefID converts a hex to base62 -func (pm *Metadata) GenerateRefID() error { - if len(pm.SettlementID) == 0 || len(pm.PayerID) == 0 { - return errors.New("must populate SettlementID and PayerID to generate a ref ID") - } - key := pm.SettlementID + pm.PayerID - bytes := sha256.Sum256([]byte(key)) - refID := base58.Encode(bytes[:], base58.BitcoinAlphabet) - refID = refID[:30] - pm.RefID = refID - return nil -} - -// FIXME make this a generic merged payment - -// Metadata holds metadata to create a row for paypal -type Metadata struct { - Amount decimal.Decimal - BATAmount decimal.Decimal - Currency string - ExecutedAt time.Time - Transactions []custodian.Transaction - ChannelCount int - PayerID string - RefID string - SettlementID string -} - -// AddTransaction to the aggregate payment -func (pm *Metadata) AddTransaction(transaction custodian.Transaction) error { - if pm.Currency != transaction.Currency { - return errors.New("currency in aggregate payment did not match existing currency") - } - pm.ChannelCount++ - pm.BATAmount = pm.BATAmount.Add(altcurrency.BAT.FromProbi(transaction.Probi)) - pm.Amount = pm.Amount.Add(transaction.Amount) - pm.Transactions = append(pm.Transactions, transaction) - return nil -} - -func exchangeFromProbi(probi decimal.Decimal, rate decimal.Decimal, currency string) decimal.Decimal { - scale := decimal.NewFromFloat(supportedCurrencies[currency]) - factor := decimal.NewFromFloat(10).Pow(scale) - return altcurrency.BAT. - FromProbi(rate.Mul(probi)). - Mul(factor). - Floor(). - Div(factor) -} - -// MassPayRow is the structure of a row used for paypal web mass pay -type MassPayRow struct { - PayerID string `csv:"Email/Phone"` - Amount decimal.Decimal `csv:"Amount"` - Currency string `csv:"Currency code"` - ID string `csv:"Reference ID"` - Note string `csv:"Note to recipient"` - DestinationType string `csv:"Recipient wallet"` -} - -// ToMassPayCSVRow turns a paypal metadata into a MassPayRow -func (pm *Metadata) ToMassPayCSVRow() *MassPayRow { - return &MassPayRow{ - PayerID: pm.PayerID, - Amount: pm.Amount, - Currency: pm.Currency, - ID: pm.RefID, - Note: fmt.Sprintf("You earned %s BAT Points, as %s %s from %d channel(s).", pm.BATAmount.String(), pm.Amount.String(), currencySymbols[pm.Currency], pm.ChannelCount), - DestinationType: "PayPal", - } -} diff --git a/tools/settlement/paypal/transform.go b/tools/settlement/paypal/transform.go deleted file mode 100644 index 42afdc8cd..000000000 --- a/tools/settlement/paypal/transform.go +++ /dev/null @@ -1,84 +0,0 @@ -package paypal - -import ( - "context" - "errors" - "time" - - "github.com/brave-intl/bat-go/libs/clients/ratios" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/shopspring/decimal" -) - -// CalculateTransactionAmounts calculates the amount for each payout given a currency and rate -func CalculateTransactionAmounts(currency string, rate decimal.Decimal, payouts *[]custodian.Transaction) (*[]custodian.Transaction, error) { - txs := make([]custodian.Transaction, 0) - for _, tx := range *payouts { - if tx.WalletProvider != "paypal" { - continue - } - tx.Amount = exchangeFromProbi(tx.Probi, rate, currency) - tx.Currency = currency - txs = append(txs, tx) - } - return &txs, nil -} - -// MergeAndTransformPayouts merges payouts to the same destination and transforms to paypal txn metadata -func MergeAndTransformPayouts(batPayouts *[]custodian.Transaction) (*[]Metadata, error) { - executedAt := time.Now().UTC() - rows := make([]Metadata, 0) - destinationToRow := map[string]*Metadata{} - - // FIXME refactor to separate merge and transform - for _, batPayout := range *batPayouts { - destination := batPayout.Destination - - var row *Metadata - var ok bool - if row, ok = destinationToRow[destination]; !ok { - row = &Metadata{ - Currency: batPayout.Currency, - ExecutedAt: executedAt, - PayerID: batPayout.Destination, - SettlementID: batPayout.SettlementID, - } - err := row.GenerateRefID() - if err != nil { - return nil, err - } - destinationToRow[destination] = row - } - - err := row.AddTransaction(batPayout) - if err != nil { - return nil, err - } - } - for _, row := range destinationToRow { - rows = append(rows, *row) - } - return &rows, nil -} - -// GetRate figures out which rate to use -func GetRate(ctx context.Context, currency string, rate decimal.Decimal) (decimal.Decimal, error) { - if rate.Equal(decimal.NewFromFloat(0)) { - client, err := ratios.NewWithContext(ctx) - if err != nil { - return rate, err - } - rateData, err := client.FetchRate(ctx, "BAT", currency) - if err != nil { - return rate, err - } - if rateData == nil { - return rate, errors.New("ratio not found") - } - rate = rateData.Payload[currency] - if time.Since(rateData.LastUpdated).Minutes() > 5 { - return rate, errors.New("ratios data is too old. update ratios response before moving forward") - } - } - return rate, nil -} diff --git a/tools/settlement/settlement.go b/tools/settlement/settlement.go deleted file mode 100644 index 073ab2246..000000000 --- a/tools/settlement/settlement.go +++ /dev/null @@ -1,451 +0,0 @@ -package settlement - -// NOTE it is important to use submit then confirm to avoid the possibility of duplicate transfers -// due to transient network errors (if retries are enabled) - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/custodian" - errorutils "github.com/brave-intl/bat-go/libs/errors" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - sentry "github.com/getsentry/sentry-go" - "github.com/shopspring/decimal" -) - -const maxConfirmTries = 5 - -// AntifraudTransaction is a "v2" transaction, creators only atm -type AntifraudTransaction struct { - custodian.Transaction - BAT decimal.Decimal `json:"bat,omitempty"` - PayoutReportID string `json:"payout_report_id,omitempty"` - WalletProviderInfo string `json:"wallet_provider_id,omitempty"` -} - -// AggregateTransaction is a single transaction aggregating multiple input transactions -type AggregateTransaction struct { - custodian.Transaction - Inputs []custodian.Transaction `json:"inputs"` - SourceFrom string `json:"source"` -} - -// ProviderInfo holds information parsed from the wallet_provider_id -type ProviderInfo struct { - Establishment string - Type string - ID string -} - -// ProviderInfo splits apart provider info from wallet provider id -func (at AntifraudTransaction) ProviderInfo() ProviderInfo { - establishmentSplit := strings.Split(at.WalletProviderInfo, "#") - establishment := establishmentSplit[0] - typeAndID := establishmentSplit[1] - typeAndIDSplit := strings.Split(typeAndID, ":") - return ProviderInfo{ - Establishment: establishment, - Type: typeAndIDSplit[0], - ID: typeAndIDSplit[1], - } -} - -// ToTransaction turns the antifraud transaction into a transaction understandable by settlement -// tools -func (at AntifraudTransaction) ToTransaction() (custodian.Transaction, error) { - t := at.Transaction - - if len(at.Destination) == 0 { - return t, errors.New("Invalid address") - } - - if at.BAT.GreaterThan(decimal.Zero) { - if len(at.WalletProviderInfo) == 0 { - return t, errors.New("Invalid wallet provider info") - - } - alt := altcurrency.BAT - providerInfo := at.ProviderInfo() - - t.Probi = alt.ToProbi(at.BAT) - t.AltCurrency = &alt - t.Amount = at.BAT - t.Currency = alt.String() - t.BATPlatformFee = alt.ToProbi(at.BATPlatformFee) - t.WalletProvider = providerInfo.Establishment - t.WalletProviderID = providerInfo.ID - t.SettlementID = at.PayoutReportID - } else if at.Probi.GreaterThan(decimal.Zero) { - t.Amount = t.AltCurrency.FromProbi(at.Probi) - } - - if len(t.WalletProviderID) == 0 { - return t, errors.New("Invalid wallet provider id") - } - - if !t.Amount.GreaterThan(decimal.NewFromFloat(0)) { - return t, errors.New("Invalid amount, is not greater than 0") - } - - return t, nil -} - -// State contains the current state of the settlement, including wallet and transaction status -type State struct { - WalletInfo wallet.Info `json:"walletInfo"` - Transactions []custodian.Transaction `json:"transactions"` -} - -// CheckForDuplicates in a list of transactions -func CheckForDuplicates(transactions []AntifraudTransaction) error { - channelSet := map[string]bool{} - for _, settlementTransaction := range transactions { - if _, exists := channelSet[settlementTransaction.Channel]; exists { - return errors.New( - "DO NOT PROCEED WITH PAYOUT: Malformed settlement file, duplicate payments detected!:" + settlementTransaction.Channel) - } - channelSet[settlementTransaction.Channel] = true - } - return nil -} - -// PrepareTransactions by embedding signed transactions into the settlement documents -func PrepareTransactions( - wallet *uphold.Wallet, - settlements []custodian.Transaction, - purpose string, - beneficiary *uphold.Beneficiary, -) error { - for i := 0; i < len(settlements); i++ { - settlement := &settlements[i] - - // Use the Note field if it exists, otherwise use the settlement ID - message := settlement.SettlementID - if len(settlement.Note) > 0 { - message = settlement.Note - } - tx, err := wallet.PrepareTransaction( - *settlement.AltCurrency, - settlement.Probi, - settlement.Destination, - message, - purpose, - beneficiary, - ) - if err != nil { - return err - } - settlement.SignedTx = tx - } - return nil -} - -func checkTransactionAgainstSettlement(settlement *custodian.Transaction, txInfo *wallet.TransactionInfo) error { - if *settlement.AltCurrency != altcurrency.BAT { - return errors.New("only settlements of BAT are supported") - } - // and that the important parts match the rest of the settlement document - if !settlement.Probi.Equals(txInfo.Probi) { - return errors.New("embedded signed transaction probi value does not match") - } - if len(txInfo.Destination) > 0 && settlement.Destination != txInfo.Destination { - return errors.New("embedded signed transaction destination address does not match") - } - - return nil -} - -// CheckPreparedTransactions performs sanity checks on an array of signed settlements -func CheckPreparedTransactions( - ctx context.Context, - settlementWallet *uphold.Wallet, - settlements []custodian.Transaction, -) error { - sumProbi := decimal.Zero - for i := 0; i < len(settlements); i++ { - settlement := &settlements[i] - - // make sure the signed transaction is well formed and the signature is valid - txInfo, err := settlementWallet.VerifyTransaction(ctx, settlement.SignedTx) - if err != nil { - return err - } - - err = checkTransactionAgainstSettlement(settlement, txInfo) - if err != nil { - return err - } - - sumProbi = sumProbi.Add(settlement.Probi) - } - - // check balance before starting payout - balance, err := settlementWallet.GetBalance(ctx, true) - if err != nil { - return err - } - if sumProbi.GreaterThan(balance.SpendableProbi) { - return errors.New("settlement wallet lacks enough funds to fulfill payout") - } - - return nil -} - -// SubmitPreparedTransaction submits a single settlement transaction to uphold -// It is designed to be idempotent across multiple runs, in case of network outage transactions that -// were unable to be submitted during an initial run can be submitted in subsequent runs. -func SubmitPreparedTransaction( - ctx context.Context, - settlementWallet *uphold.Wallet, - settlement *custodian.Transaction, -) error { - logger := logging.Logger(ctx, "settlement.SubmitPreparedTransaction") - if settlement.IsComplete() { - logger.Info().Msg(fmt.Sprintf("already complete, skipping submit for channel %s", settlement.Channel)) - return nil - } - if settlement.IsFailed() { - logger.Info().Msg(fmt.Sprintf("already failed, skipping submit for channel %s", settlement.Channel)) - return nil - } - - if len(settlement.ProviderID) > 0 { - // first check if the transaction has already been confirmed - upholdInfo, err := settlementWallet.GetTransaction(ctx, settlement.ProviderID) - if err == nil { - settlement.Status = upholdInfo.Status - settlement.Currency = upholdInfo.DestCurrency - settlement.Amount = upholdInfo.DestAmount - settlement.TransferFee = upholdInfo.TransferFee - settlement.ExchangeFee = upholdInfo.ExchangeFee - - if settlement.IsComplete() { - logger.Info().Msg(fmt.Sprintf("transaction already complete for channel %s", settlement.Channel)) - return nil - } - } else if errorutils.IsErrNotFound(err) { // unconfirmed transactions appear as "not found" - if time.Now().UTC().Before(settlement.ValidUntil) { - return nil - } - msg := fmt.Sprintf("already submitted, but quote has expired for channel %s", settlement.Channel) - logger.Info().Msg(msg) - settlement.FailureReason = msg - } else { - return err - } - } - - // post the settlement to uphold but do not confirm it - submitInfo, err := settlementWallet.SubmitTransaction(ctx, settlement.SignedTx, false) - if errorutils.IsErrInvalidDestination(err) { - msg := "invalid destination, skipping" - logger.Info().Msg(msg) - settlement.Status = "failed" - settlement.FailureReason = msg - return nil - } else if err != nil { - return err - } - - if time.Now().UTC().Equal(settlement.ValidUntil) || time.Now().UTC().After(settlement.ValidUntil) { - // BAT transfers have TTL of zero, as do invalid transfers of XAU / LBA - if submitInfo.DestCurrency == "XAU" || submitInfo.DestCurrency == "LBA" { - msg := "quote returned is invalid, skipping" - logger.Info().Msg(msg) - settlement.Status = "failed" - settlement.FailureReason = msg - return nil - } - } - - logger.Info().Msg(fmt.Sprintf("transaction for channel %s submitted, new quote acquired", settlement.Channel)) - - settlement.ProviderID = submitInfo.ID - settlement.Status = submitInfo.Status - settlement.ValidUntil = submitInfo.ValidUntil - return nil -} - -// SubmitPreparedTransactions by submitting them to uphold after performing sanity checks -// It is designed to be idempotent across multiple runs, in case of network outage transactions that -// were unable to be submitted during an initial run can be submitted in subsequent runs. -func SubmitPreparedTransactions( - ctx context.Context, - settlementWallet *uphold.Wallet, - settlements []custodian.Transaction, -) error { - err := CheckPreparedTransactions(ctx, settlementWallet, settlements) - if err != nil { - return err - } - - for i := 0; i < len(settlements); i++ { - err = SubmitPreparedTransaction(ctx, settlementWallet, &settlements[i]) - if err != nil { - return err - } - } - - return nil -} - -// ConfirmPreparedTransaction confirms a single settlement transaction with uphold -// It is designed to be idempotent across multiple runs, in case of network outage transactions that -// were unable to be confirmed during an initial run can be submitted in subsequent runs. -func ConfirmPreparedTransaction( - ctx context.Context, - settlementWallet *uphold.Wallet, - settlement *custodian.Transaction, - isResubmit bool, -) error { - var ( - settlementInfo *wallet.TransactionInfo - err error - ) - logger := logging.Logger(ctx, "settlement.ConfirmPreparedTransaction") - for tries := maxConfirmTries; tries >= 0; tries-- { - if tries == 0 { - baseMsg := "could not confirm settlement payout after multiple tries: %+v" - logger.Info().Msg(fmt.Sprintf("%s for channel %s", baseMsg, settlement.Channel)) - sentry.CaptureException(fmt.Errorf(baseMsg, map[string]string{ - "tries": strconv.Itoa(maxConfirmTries - tries), - "channel": settlement.Channel, - "hash": settlement.ProviderID, - "publisher": settlement.Publisher, - "settlementId": settlement.SettlementID, - "status": settlement.Status, - })) - } - - if settlement.IsComplete() { - logger.Info().Msg(fmt.Sprintf("already complete, skipping confirm for destination %s", settlement.Destination)) - return nil - } - if settlement.IsFailed() { - logger.Info().Msg(fmt.Sprintf("already failed, skipping confirm for destination %s", settlement.Destination)) - return nil - } - - if isResubmit { - logger.Info().Msg(fmt.Sprintf("attempting resubmission of transaction for destination: %s", settlement.Destination)) - // first check if the transaction has already been confirmed - upholdInfo, err := settlementWallet.GetTransaction(ctx, settlement.ProviderID) - if err == nil { - settlement.Status = upholdInfo.Status - settlement.Currency = upholdInfo.DestCurrency - settlement.Amount = upholdInfo.DestAmount - settlement.TransferFee = upholdInfo.TransferFee - settlement.ExchangeFee = upholdInfo.ExchangeFee - - if !settlement.IsComplete() { - logger.Info().Msg(fmt.Sprintf("error transaction status is: %s", upholdInfo.Status)) - } - - break - - } else if errorutils.IsErrNotFound(err) { // unconfirmed transactions appear as "not found" - if time.Now().UTC().After(settlement.ValidUntil) { - logger.Info().Msg(fmt.Sprintf("quote has expired, must resubmit transaction for channel %s", settlement.Channel)) - return nil - } - } - } - - settlementInfo, err = settlementWallet.ConfirmTransaction(ctx, settlement.ProviderID) - if err == nil { - logger.Info().Msg(fmt.Sprintf("transaction confirmed for destination: %s", settlement.Destination)) - settlement.Status = settlementInfo.Status - settlement.Currency = settlementInfo.DestCurrency - settlement.Amount = settlementInfo.DestAmount - settlement.TransferFee = settlementInfo.TransferFee - settlement.ExchangeFee = settlementInfo.ExchangeFee - - // do a sanity check that the uphold transaction confirmed referenced matches the settlement object - err = checkTransactionAgainstSettlement(settlement, settlementInfo) - if err != nil { - return err - } - - break - } else if errorutils.IsErrForbidden(err) { - logger.Error().Err(err).Msg("invalid destination, skipping") - settlement.Status = "failed" - return nil - } else if errorutils.IsErrNotFound(err) { - logger.Error().Err(err).Msg("transaction not found, skipping") - settlement.Status = "failed" - return nil - } else if errorutils.IsErrAlreadyExists(err) { - // NOTE we've observed the uphold API LB timing out while the request is eventually processed - upholdInfo, err := settlementWallet.GetTransaction(ctx, settlement.ProviderID) - if err == nil { - settlement.Status = upholdInfo.Status - settlement.Currency = upholdInfo.DestCurrency - settlement.Amount = upholdInfo.DestAmount - settlement.TransferFee = upholdInfo.TransferFee - settlement.ExchangeFee = upholdInfo.ExchangeFee - - if !settlement.IsComplete() { - logger.Info().Msg(fmt.Sprintf("error transaction status is: %s", upholdInfo.Status)) - } - } - settlement.Status = "complete" - break - } else { - logger.Info().Msg(fmt.Sprintf("error confirming: %s", err)) - } - } - return nil -} - -// ConfirmPreparedTransactions confirms settlement transactions that have already been submitted to uphold -// It is designed to be idempotent across multiple runs, in case of network outage transactions that -// were unable to be confirmed during an initial run can be confirmed in subsequent runs. -func ConfirmPreparedTransactions(ctx context.Context, settlementWallet *uphold.Wallet, settlements []custodian.Transaction) error { - for i := 0; i < len(settlements); i++ { - err := ConfirmPreparedTransaction(ctx, settlementWallet, &settlements[i], false) - if err != nil { - return err - } - } - - return nil -} - -// BPTSignedSettlement is a struct describing the signed output format of brave-payment-tools -type BPTSignedSettlement struct { - SignedTxs []struct { - httpsignature.HTTPSignedRequest `json:"signedTx"` - } `json:"signedTxs"` -} - -// ParseBPTSignedSettlement parses the signed output from brave-payment-tools -// It returns an array of base64 encoded "extracted" httpsignatures -func ParseBPTSignedSettlement(jsonIn []byte) ([]string, error) { - var s BPTSignedSettlement - err := json.Unmarshal(jsonIn, &s) - if err != nil { - return nil, err - } - var encoded []string - for i := range s.SignedTxs { - b, err := json.Marshal(s.SignedTxs[i].HTTPSignedRequest) - if err != nil { - return nil, err - } - encoded = append(encoded, base64.StdEncoding.EncodeToString(b)) - } - - return encoded, nil -} diff --git a/tools/settlement/settlement_test.go b/tools/settlement/settlement_test.go deleted file mode 100644 index b3a89910a..000000000 --- a/tools/settlement/settlement_test.go +++ /dev/null @@ -1,357 +0,0 @@ -package settlement - -import ( - "context" - "encoding/hex" - "encoding/json" - "os" - "testing" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/shopspring/decimal" - "golang.org/x/crypto/ed25519" -) - -func TestTransactions(t *testing.T) { - ctx := context.Background() - - if os.Getenv("UPHOLD_ACCESS_TOKEN") == "" { - t.Skip("skipping test; UPHOLD_ACCESS_TOKEN not set") - } - if os.Getenv("DONOR_WALLET_PUBLIC_KEY") == "" { - t.Skip("skipping test; DONOR_WALLET_PUBLIC_KEY not set") - } - if os.Getenv("DONOR_WALLET_PRIVATE_KEY") == "" { - t.Skip("skipping test; DONOR_WALLET_PRIVATE_KEY not set") - } - if os.Getenv("DONOR_WALLET_CARD_ID") == "" { - t.Skip("skipping test; DONOR_WALLET_CARD_ID not set") - } - if os.Getenv("QA_PUBLISHER_USD_CARD_ID") == "" { - t.Skip("skipping test; QA_PUBLISHER_USD_CARD_ID not set") - } - - usdCard := os.Getenv("QA_PUBLISHER_USD_CARD_ID") - - var donorInfo wallet.Info - donorInfo.Provider = "uphold" - donorInfo.ProviderID = os.Getenv("DONOR_WALLET_CARD_ID") - { - tmp := altcurrency.BAT - donorInfo.AltCurrency = &tmp - } - - donorWalletPublicKeyHex := os.Getenv("DONOR_WALLET_PUBLIC_KEY") - donorWalletPrivateKeyHex := os.Getenv("DONOR_WALLET_PRIVATE_KEY") - var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey - donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) - if err != nil { - t.Fatal(err) - } - donorPrivateKey, err = hex.DecodeString(donorWalletPrivateKeyHex) - if err != nil { - t.Fatal(err) - } - donorWallet := &uphold.Wallet{Info: donorInfo, PrivKey: donorPrivateKey, PubKey: donorPublicKey} - - if len(donorWallet.ID) > 0 { - t.Fatal("FIXME") - } - - settlementJSON := []byte(` - [ - { - "address": "` + usdCard + `", - "altcurrency": "BAT", - "authority": "github:evq", - "currency": "BAT", - "fees": "1339169009771847163", - "owner": "publishers#uuid:23813236-3f4c-40dc-916e-8f55c8865b5a", - "probi": "25444211185665096101", - "publisher": "example.com", - "transactionId": "0f7377cc-73ef-4e94-b69a-7086a4f3b2a8", - "type": "referral" - } - ] - `) - - var settlements []custodian.Transaction - err = json.Unmarshal(settlementJSON, &settlements) - if err != nil { - t.Fatal(err) - } - - err = PrepareTransactions(donorWallet, settlements, "", nil) - if err != nil { - t.Fatal(err) - } - - err = SubmitPreparedTransactions(ctx, donorWallet, settlements) - if err != nil { - t.Fatal(err) - } - - var hashes []string - for i := 0; i < len(settlements); i++ { - hashes = append(hashes, settlements[i].ProviderID) - } - - // Multiple submit should have no effect - err = SubmitPreparedTransactions(ctx, donorWallet, settlements) - if err != nil { - t.Fatal(err) - } - - for i := 0; i < len(settlements); i++ { - if hashes[i] != settlements[i].ProviderID { - t.Fatal("Hash for settlement failed") - } - } - - err = ConfirmPreparedTransactions(ctx, donorWallet, settlements) - if err != nil { - t.Fatal(err) - } - - for i := 0; i < len(settlements); i++ { - var txInfo *wallet.TransactionInfo - txInfo, err = donorWallet.GetTransaction(ctx, settlements[i].ProviderID) - if err != nil { - t.Fatal(err) - } - if err = checkTransactionAgainstSettlement(&settlements[i], txInfo); err != nil { - t.Error("Uphold transaction referenced in settlement should match!") - t.Fatal(err) - } - } - - // Multiple confirm should not error - err = ConfirmPreparedTransactions(ctx, donorWallet, settlements) - if err != nil { - t.Fatal(err) - } -} - -func TestDeterministicSigning(t *testing.T) { - usdCard := "03aeafb8-555d-4840-90d1-ff0f99426475" - - var donorInfo wallet.Info - donorInfo.Provider = "uphold" - donorInfo.ProviderID = "aea53308-9b35-4f63-bccd-9f1dffa3d8c0" - { - tmp := altcurrency.BAT - donorInfo.AltCurrency = &tmp - } - - donorWalletPublicKeyHex := "10ba999b2b7b9eabc0f44fa26bf122ebbfa98dc6fef31e6251a9c1c58d60bb8d" - donorWalletPrivateKeyHex := "8d6a620a566e094cebaec67edca32a68efce962890570157f0b8a5389cc5f6df10ba999b2b7b9eabc0f44fa26bf122ebbfa98dc6fef31e6251a9c1c58d60bb8d" - var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey - donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) - if err != nil { - t.Fatal(err) - } - donorPrivateKey, err = hex.DecodeString(donorWalletPrivateKeyHex) - if err != nil { - t.Fatal(err) - } - donorWallet := &uphold.Wallet{Info: donorInfo, PrivKey: donorPrivateKey, PubKey: donorPublicKey} - - if len(donorWallet.ID) > 0 { - t.Fatal("FIXME") - } - - settlementJSON := []byte(` - [ - { - "address": "` + usdCard + `", - "altcurrency": "BAT", - "authority": "github:evq", - "currency": "BAT", - "fees": "1339169009771847163", - "owner": "publishers#uuid:23813236-3f4c-40dc-916e-8f55c8865b5a", - "probi": "25444211185665096101", - "publisher": "example.com", - "transactionId": "0f7377cc-73ef-4e94-b69a-7086a4f3b2a8", - "type": "referral" - } - ] - `) - - bravePaymentToolsSignedSettlement := []byte(` - { - "config": { - "env": "test" - }, - "signedTxs": [ - { - "id": "f042845f-fa62-4022-8117-a476ec583a7b", - "requestType": "httpSignature", - "signedTx": { - "headers": { - "digest": "SHA-256=zrtB9DhyDmPLMml/JwBJ3rnVyzBYhBGgoYiGaL5msYI=", - "signature": "keyId=\"primary\",algorithm=\"ed25519\",headers=\"digest\",signature=\"1n4soEhMbhhHHk2IZ9xkVsaFRj9ajD6+y4MEzl8FcxZTviy5utHIKugPiFMQvSaktegvA5NIs3wNGFsuk4OtBQ==\"" - }, - "body": { - "denomination": { - "amount": "25.444211185665096101", - "currency": "BAT" - }, - "destination": "03aeafb8-555d-4840-90d1-ff0f99426475", - "message": "0f7377cc-73ef-4e94-b69a-7086a4f3b2a8" - }, - "octets": "{\"denomination\":{\"amount\":\"25.444211185665096101\",\"currency\":\"BAT\"},\"destination\":\"03aeafb8-555d-4840-90d1-ff0f99426475\",\"message\":\"0f7377cc-73ef-4e94-b69a-7086a4f3b2a8\"}" - } - } - ], - "authenticate": {} - } - `) - - knownSigs, err := ParseBPTSignedSettlement(bravePaymentToolsSignedSettlement) - if err != nil { - t.Fatal(err) - } - - var settlements []custodian.Transaction - err = json.Unmarshal(settlementJSON, &settlements) - if err != nil { - t.Fatal(err) - } - - err = PrepareTransactions(donorWallet, settlements, "", nil) - if err != nil { - t.Fatal(err) - } - for i := range settlements { - if settlements[i].SignedTx != knownSigs[i] { - t.Fatal("Signature does not match equivalent from brave-payment-tools") - } - } -} - -func TestUnmarshalAdsTransaction(t *testing.T) { - settlementJSON := []byte(` - [ - { - "address": "5e14c5b2-8651-427d-905e-b078513b6fc3", - "altcurrency": "BAT", - "currency": "BAT", - "fees": "0", - "note": "payout for test ad earnings", - "probi": "25444211185665096101", - "publisher": "wallet:6e4111a8-0946-4dff-8010-65272bc5b7bb", - "transactionId": "0f7377cc-73ef-4e94-b69a-7086a4f3b2a8", - "type": "adsDirectDeposit", - "walletProvider": "uphold", - "walletProviderId": "6ff1c5af-5943-4e01-bc2c-88a8881c3606" - }, - { - "address": "5e14c5b2-8651-427d-905e-b078513b6fc3", - "altcurrency": "BAT", - "currency": "BAT", - "fees": "0", - "note": "payout for test ad earnings", - "probi": "25444211185665096101", - "publisher": "wallet:6e4111a8-0946-4dff-8010-65272bc5b7bb", - "transactionId": "0f7377cc-73ef-4e94-b69a-7086a4f3b2a8", - "type": "adsDirectDeposit", - "walletProvider": "uphold", - "walletProviderId": "6ff1c5af-5943-4e01-bc2c-88a8881c3606" - } - ] - `) - - var afTransactions []AntifraudTransaction - err := json.Unmarshal(settlementJSON, &afTransactions) - if err != nil { - t.Fatal(err) - } - - var settlements []custodian.Transaction - for _, afTxn := range afTransactions { - txn, err := afTxn.ToTransaction() - if err != nil { - t.Fatal(err) - } - settlements = append(settlements, txn) - } - - expected, err := decimal.NewFromString("25.444211185665096101") - if err != nil { - t.Fatal(err) - } - if !settlements[0].Amount.Equals(expected) { - t.Fatal("Amount does not match") - } -} - -func TestUnmarshalCreatorsTransaction(t *testing.T) { - settlementJSON := []byte(` - { - "address": "5e14c5b2-8651-427d-905e-b078513b6fc3", - "bat": "0.712500000000000000", - "channel_type": "TwitchChannelDetails", - "created_at": "2021-11-09 02:20:47.586946+00:00", - "fees": "0.037500000000000006", - "id": "c6d5983b-fef7-4e7d-b0a3-5bf68e0bd95a", - "inserted_at": "2021-11-08 21:53:30.146504+00:00", - "owner": "publishers#uuid:008c092b-7afa-47f0-9677-22538d9abc3d", - "owner_state": "active", - "payout_report_id": "4520e913-664e-479e-a58c-357cf750b00a", - "publisher": "twitch#author:meliunu", - "type": "contribution", - "url": "https://twitch.tv/meliunu", - "wallet_country_code": "CL", - "wallet_provider": "0", - "wallet_provider_id": "uphold#id:6c0397f3-df41-440a-9fbb-b517e1142a9a" - } - `) - - var settlement AntifraudTransaction - err := json.Unmarshal(settlementJSON, &settlement) - if err != nil { - t.Fatal(err) - } - - txn, err := settlement.ToTransaction() - if err != nil { - t.Fatal(err) - } - out, err := json.MarshalIndent(txn, "", " ") - if err != nil { - t.Fatal(err) - } - - expected := []byte(`{ - "altcurrency": "BAT", - "authority": "", - "amount": "0.7125", - "commission": "0", - "currency": "BAT", - "address": "5e14c5b2-8651-427d-905e-b078513b6fc3", - "owner": "publishers#uuid:008c092b-7afa-47f0-9677-22538d9abc3d", - "fees": "37500000000000006", - "probi": "712500000000000000", - "hash": "", - "walletProvider": "uphold", - "walletProviderId": "6c0397f3-df41-440a-9fbb-b517e1142a9a", - "publisher": "twitch#author:meliunu", - "signedTx": "", - "status": "", - "transactionId": "4520e913-664e-479e-a58c-357cf750b00a", - "fee": "0", - "type": "contribution", - "validUntil": "0001-01-01T00:00:00Z", - "note": "" -}`) - - if string(out) != string(expected) { - t.Fatal("Converted transaction does not match") - } -} diff --git a/tools/settlement/uphold/prepare.go b/tools/settlement/uphold/prepare.go deleted file mode 100644 index 4cb97b3db..000000000 --- a/tools/settlement/uphold/prepare.go +++ /dev/null @@ -1,49 +0,0 @@ -package uphold - -import ( - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/shopspring/decimal" -) - -// GroupSettlements groups settlements under a single wallet provider id so that we can impose limits based on price -// no signing here, just grouping settlements under a single deposit id -func GroupSettlements( - settlements *[]custodian.Transaction, -) map[string][]custodian.Transaction { - groupedByWalletProviderID := make(map[string][]custodian.Transaction) - - for _, payout := range *settlements { - if payout.WalletProvider == "uphold" { - walletProviderID := payout.WalletProviderID - if groupedByWalletProviderID[walletProviderID] == nil { - groupedByWalletProviderID[walletProviderID] = []custodian.Transaction{} - } - groupedByWalletProviderID[walletProviderID] = append(groupedByWalletProviderID[walletProviderID], payout) - } - } - return groupedByWalletProviderID -} - -// FlattenPaymentsByWalletProviderID returns settlements with any instances where a single -// Uphold wallet is getting multiple payments as a single instance with the amounts -// summed. This decreases the total number of distinct transactions that have to be sent -// without impacting the payout amount for any given user. -func FlattenPaymentsByWalletProviderID(settlements *[]custodian.Transaction) []custodian.Transaction { - groupedSettlements := GroupSettlements(settlements) - flattenedSettlements := []custodian.Transaction{} - for _, v := range groupedSettlements { - var ( - flattenedSettlement custodian.Transaction - flattenedSettlementProbi decimal.Decimal = decimal.NewFromFloat(0.0) - ) - for _, record := range v { - if (flattenedSettlement == custodian.Transaction{}) { - flattenedSettlement = record - } - flattenedSettlementProbi = flattenedSettlementProbi.Add(record.Probi) - } - flattenedSettlement.Probi = flattenedSettlementProbi - flattenedSettlements = append(flattenedSettlements, flattenedSettlement) - } - return flattenedSettlements -} diff --git a/tools/settlement/uphold/prepare_test.go b/tools/settlement/uphold/prepare_test.go deleted file mode 100644 index 21629e922..000000000 --- a/tools/settlement/uphold/prepare_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package uphold - -import ( - "fmt" - "math/rand" - "reflect" - "testing" - "time" - - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/shopspring/decimal" - "gotest.tools/assert" -) - -// TestGroupSettlements tests GroupSettlements -func TestGroupSettlements(t *testing.T) { - settlements, wantedSettlements := generateRandomSettlementsAndResultMap() - result := GroupSettlements(&settlements) - if !reflect.DeepEqual(result, wantedSettlements) { - t.Fatalf("wanted: %#v\nfound: %#v", wantedSettlements, result) - } -} - -// TestFlattenPaymentsByWalletProviderID tests FlattenPaymentsByWalletProviderID -func TestFlattenPaymentsByWalletProviderID(t *testing.T) { - settlements, wantedSettlements := generateFixedSettlementsSliceAndResultsSlice() - results := FlattenPaymentsByWalletProviderID(&settlements) - foundMatches := 0 - for _, result := range results { - for _, wantedSettlement := range wantedSettlements { - fmt.Printf("wanted WalletProviderID: %s\nfound WalletProviderID: %s\n", wantedSettlement.WalletProviderID, result.WalletProviderID) - if wantedSettlement.WalletProviderID == result.WalletProviderID { - fmt.Printf("wanted Amount: %s\nfound Amount: %s\n", wantedSettlement.Probi, result.Probi) - if wantedSettlement.Probi.Equal(result.Probi) { - foundMatches++ - } - } - } - } - assert.Equal(t, foundMatches, len(wantedSettlements)) -} - -func generateRandomSettlementsAndResultMap() ([]custodian.Transaction, map[string][]custodian.Transaction) { - var ( - settlements []custodian.Transaction - wantedSettlements = make(map[string][]custodian.Transaction) - ) - altCurrency := altcurrency.BAT - provderIds := []string{"ea524fe8-8a81-4191-a1bf-610f4b956816", "fc5cd231-22ac-403b-867c-9b8a080001b2", "060f9494-9b71-450b-89a7-ba745a9f36f7", "fe3e49fe-9c5c-4b08-b07c-97b3171c0b69"} - // Push a random number of records for each provderId into the settlements slice - // At the same time, construct the expected results map. This is done with random - // numbers of settlements to mitigate programming to the test. - for _, provderID := range provderIds { - settlementInstance := custodian.Transaction{ - AltCurrency: &altCurrency, - Authority: "test", - Amount: decimal.NewFromFloat(0.0), - ExchangeFee: decimal.NewFromFloat(0.0), - FailureReason: "test", - Currency: "test", - Destination: "test", - Publisher: "test", - BATPlatformFee: decimal.NewFromFloat(0.0), - Probi: decimal.NewFromFloat(0.0), - ProviderID: "test", - WalletProvider: "uphold", - WalletProviderID: provderID, - Channel: "test", - SignedTx: "test", - Status: "test", - SettlementID: "test", - TransferFee: decimal.NewFromFloat(0.0), - Type: "test", - ValidUntil: time.Now(), - DocumentID: "test", - Note: "test", - } - - for i := 0; i <= rand.Intn(5); i++ { - settlements = append(settlements, settlementInstance) - if wantedSettlements[provderID] != nil { - wantedSettlements[provderID] = append( - wantedSettlements[provderID], - settlementInstance, - ) - } else { - wantedSettlements[provderID] = []custodian.Transaction{ - settlementInstance, - } - } - } - } - return settlements, wantedSettlements -} - -func generateFixedSettlementsSliceAndResultsSlice() ([]custodian.Transaction, []custodian.Transaction) { - var ( - settlements []custodian.Transaction - wantedSettlements []custodian.Transaction - ) - altCurrency := altcurrency.BAT - provderIds := []string{"ea524fe8-8a81-4191-a1bf-610f4b956816", "fc5cd231-22ac-403b-867c-9b8a080001b2", "060f9494-9b71-450b-89a7-ba745a9f36f7", "fe3e49fe-9c5c-4b08-b07c-97b3171c0b69"} - - probi1, _ := decimal.NewFromString("1000000000000000000") - probi2, _ := decimal.NewFromString("200000000000000000") - probi3, _ := decimal.NewFromString("500000000000000000") - probi4, _ := decimal.NewFromString("4100000000000000000") - probi5, _ := decimal.NewFromString("9300000000000000000") - probi6, _ := decimal.NewFromString("24100000000000000000") - probi7, _ := decimal.NewFromString("7700000000000000000") - probi8, _ := decimal.NewFromString("1000000000000000345") - priceSet := []decimal.Decimal{ - probi1, probi2, probi3, probi4, probi5, probi6, probi7, probi8, - } - wantedSum, _ := decimal.NewFromString("47900000000000000345") - presetTime := time.Now() - for _, provderID := range provderIds { - settlementInstance := custodian.Transaction{ - AltCurrency: &altCurrency, - Authority: "test", - Amount: decimal.NewFromFloat(0.0), - ExchangeFee: decimal.NewFromFloat(0.0), - FailureReason: "test", - Currency: "test", - Destination: "test", - Publisher: "test", - BATPlatformFee: decimal.NewFromFloat(0.0), - Probi: decimal.NewFromFloat(0.0), - ProviderID: "test", - WalletProvider: "uphold", - WalletProviderID: provderID, - Channel: "test", - SignedTx: "test", - Status: "test", - SettlementID: "test", - TransferFee: decimal.NewFromFloat(0.0), - Type: "test", - ValidUntil: presetTime, - DocumentID: "test", - Note: "test", - } - for _, price := range priceSet { - settlementInstance.Probi = price - settlements = append(settlements, settlementInstance) - } - settlementInstance.Probi = wantedSum - wantedSettlements = append(wantedSettlements, settlementInstance) - } - return settlements, wantedSettlements -} diff --git a/tools/vault/cmd/create_wallet.go b/tools/vault/cmd/create_wallet.go deleted file mode 100644 index d057d73d9..000000000 --- a/tools/vault/cmd/create_wallet.go +++ /dev/null @@ -1,194 +0,0 @@ -package vault - -import ( - "encoding/hex" - "encoding/json" - "fmt" - "os" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/httpsignature" - logutils "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" - "github.com/spf13/cobra" - "golang.org/x/crypto/ed25519" -) - -// State contains the current state of the registration -type State struct { - WalletInfo wallet.Info `json:"walletInfo"` - Registration string `json:"registration"` -} - -var ( - // CreateWalletCmd transfer funds command - CreateWalletCmd = &cobra.Command{ - Use: "create-wallet WALLET_NAME", - Short: "creates a wallet on a given provider", - Run: rootcmd.Perform("create wallet", CreateWallet), - } -) - -func init() { - VaultCmd.AddCommand( - CreateWalletCmd, - ) - - createWalletBuilder := cmdutils.NewFlagBuilder(CreateWalletCmd) - - createWalletBuilder.Flag().Bool("offline", false, - "operate in multi-step offline mode"). - Bind("offline") -} - -// CreateWallet creates a wallet -func CreateWallet(command *cobra.Command, args []string) error { - - ctx := command.Context() - - offline, err := command.Flags().GetBool("offline") - if err != nil { - return err - } - - // setup a new logger, add to context as well - _, logger := logutils.SetupLogger(ctx) - - name := args[0] - logFile := name + "-registration.json" - - var state State - var enc *json.Encoder - - if offline { - f, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) - if err != nil { - return err - } - - dec := json.NewDecoder(f) - - for dec.More() { - err := dec.Decode(&state) - if err != nil { - return err - } - } - - enc = json.NewEncoder(f) - } - - if len(state.WalletInfo.PublicKey) == 0 || len(state.Registration) == 0 { - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = "" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - state.WalletInfo = info - - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return err - } - - signer, err := wrappedClient.GenerateEd25519Signer(name) - if err != nil { - return err - } - - logger.Info(). - Str("provider", info.Provider). - Str("public_key", signer.String()). - Str("name", name). - Msg("keypair") - - state.WalletInfo.PublicKey = signer.String() - - wallet := &uphold.Wallet{Logger: logger, Info: state.WalletInfo, PrivKey: signer, PubKey: signer} - - reg, err := wallet.PrepareRegistration(name) - if err != nil { - return err - } - state.Registration = reg - - if offline { - err = enc.Encode(state) - if err != nil { - return err - } - logger.Info(). - Str("name", name). - Str("logfile", logFile). - Msg("success, signed registration for wallet.\nPlease copy logfile to the online machine and re-run") - return nil - } - } - - if len(state.WalletInfo.ProviderID) == 0 { - var publicKey httpsignature.Ed25519PubKey - publicKey, err := hex.DecodeString(state.WalletInfo.PublicKey) - if err != nil { - return err - } - - wallet := uphold.Wallet{Logger: logger, Info: state.WalletInfo, PrivKey: ed25519.PrivateKey{}, PubKey: publicKey} - - err = wallet.SubmitRegistration(ctx, state.Registration) - if err != nil { - return err - } - - logger.Info(). - Str("name", name). - Msg("success, registered new keypair and wallet") - logger.Info(). - Str("card_id", wallet.Info.ProviderID). - Msg("uphold") - state.WalletInfo.ProviderID = wallet.Info.ProviderID - - depositAddr, err := wallet.CreateCardAddress(ctx, "ethereum") - if err != nil { - return err - } - logger.Info(). - Str("address", depositAddr). - Str("currency", "ETH"). - Msg("created deposit addr") - - if offline { - err = enc.Encode(state) - if err != nil { - return err - } - - return fmt.Errorf("please copy %s to the offline machine and re-run", logFile) - } - } - - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return err - } - - err = wrappedClient.GenerateMounts() - if err != nil { - return err - } - - _, err = wrappedClient.Client.Logical().Write("wallets/"+name, map[string]interface{}{ - "providerId": state.WalletInfo.ProviderID, - }) - if err != nil { - return err - } - - logger.Info().Msg("Wallet setup complete!") - return nil -} diff --git a/tools/vault/cmd/import_key.go b/tools/vault/cmd/import_key.go deleted file mode 100644 index 395af6cf0..000000000 --- a/tools/vault/cmd/import_key.go +++ /dev/null @@ -1,220 +0,0 @@ -package vault - -import ( - "context" - "encoding/hex" - "errors" - "strings" - - rootcmd "github.com/brave-intl/bat-go/cmd" - - cmdutils "github.com/brave-intl/bat-go/cmd" - appctx "github.com/brave-intl/bat-go/libs/context" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - keysList = []string{ - "uphold-contribution", - "uphold-referral", - "gemini-contribution", - "gemini-referral", - } - - // ImportKeyCmd imports keys to be used in vault - ImportKeyCmd = &cobra.Command{ - Use: "import-key", - Short: "import keys to be used in vault", - Run: rootcmd.Perform("import key", ImportKey), - } -) - -func init() { - VaultCmd.AddCommand( - ImportKeyCmd, - ) - - importKeyBuilder := cmdutils.NewFlagBuilder(ImportKeyCmd) - - // wallet-refs - default to keysList above. list of known keys - // under which new wallet secrets can be referenced - importKeyBuilder.Flag().StringSlice("wallet-refs", keysList, - "the default path to a configuration file"). - Bind("wallet-refs") - - // config - importKeyBuilder.Flag().String("config", "config.yaml", - "config holds the mapping of wallet identifiers and secrets are to be held in vault"). - Bind("config") - - // ed25519-private-key - importKeyBuilder.Flag().String("ed25519-private-key", "", - "ed25519-private-key holds the private key in plaintext hex that we want to interact with"). - Bind("ed25519-private-key"). - Env("ED25519_PRIVATE_KEY") - - // ed25519-public-key - importKeyBuilder.Flag().String("ed25519-public-key", "", - "ed25519-public-key holds the public key in plaintext hex that we want to interact with"). - Bind("ed25519-public-key"). - Env("ED25519_PUBLIC_KEY") - - // uphold-provider-id - importKeyBuilder.Flag().String("uphold-provider-id", "", - "uphold-provider-id holds the uphold wallet guid that we want to interact with"). - Bind("uphold-provider-id"). - Env("UPHOLD_PROVIDER_ID") - - // gemini-client-id - importKeyBuilder.Flag().String("gemini-client-id", "", - "gemini-client-id holds the gemini oauth id used to pay transactions from a particular account"). - Bind("gemini-client-id"). - Env("GEMINI_CLIENT_ID") - - // gemini-client-key - importKeyBuilder.Flag().String("gemini-client-key", "", - "gemini-client-key holds the gemini key that is used by gemini to look up our hmac signing key"). - Bind("gemini-client-key"). - Env("GEMINI_CLIENT_KEY") - - // gemini-client-secret - importKeyBuilder.Flag().String("gemini-client-secret", "", - "gemini-client-secret holds the uphold guid that we want to use to sign bulk transactions"). - Bind("gemini-client-secret"). - Env("GEMINI_CLIENT_SECRET") -} - -// ImportKey pulls in keys from environment variables -func ImportKey(command *cobra.Command, args []string) error { - ReadConfig(command) - walletRefs := viper.GetViper().GetStringSlice("wallet-refs") - ed25519PrivateKey := viper.GetViper().GetString("ed25519-private-key") - ed25519PublicKey := viper.GetViper().GetString("ed25519-public-key") - upholdProviderID := viper.GetViper().GetString("uphold-provider-id") - geminiClientID := viper.GetViper().GetString("gemini-client-id") - geminiClientKey := viper.GetViper().GetString("gemini-client-key") - geminiClientSecret := viper.GetViper().GetString("gemini-client-secret") - - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return err - } - - for _, key := range walletRefs { - parts := strings.Split(key, "-") - switch parts[0] { - case "uphold": - if len(ed25519PrivateKey) != 0 && len(ed25519PublicKey) != 0 { - err = upholdVaultImportKey( - command.Context(), - wrappedClient, - key, - ed25519PrivateKey, - ed25519PublicKey, - upholdProviderID, - ) - if err != nil { - return err - } - } - case "gemini": - if len(geminiClientSecret) != 0 { - err = geminiVaultImportValues( - command.Context(), - wrappedClient, - key, - geminiClientID, - geminiClientKey, - geminiClientSecret, - ) - if err != nil { - return err - } - } - default: - return errors.New("did not recognize option: " + key) - } - if err != nil { - return err - } - } - return nil -} - -func upholdVaultImportKey( - ctx context.Context, - wrappedClient *vaultsigner.WrappedClient, - key string, - ed25519PrivateKey string, - ed25519PublicKey string, - upholdProviderID string, -) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return err - } - importName := Config.GetWalletKey(key) - privKey, err := hex.DecodeString(ed25519PrivateKey) - if err != nil { - return errors.New("ERROR: Key material must be passed as hex") - } - - pubKey, err := hex.DecodeString(ed25519PublicKey) - if err != nil { - return errors.New("ERROR: Key material must be passed as hex") - } - - if err := wrappedClient.GenerateMounts(); err != nil { - return err - } - logger.Info(). - Str("provider", "uphold"). - Str("config-key", key). - Str("vault-key", importName). - Int("public-length", len(pubKey)). - Int("private-length", len(privKey)). - Msg("importing secret") - _, err = wrappedClient.FromKeypair(privKey, pubKey, importName) - if err != nil { - return err - } - _, err = wrappedClient.Client.Logical().Write("wallets/"+importName, map[string]interface{}{ - "providerId": upholdProviderID, - }) - return err -} - -func geminiVaultImportValues( - ctx context.Context, - wrappedClient *vaultsigner.WrappedClient, - key string, - geminiClientID string, - geminiClientKey string, - geminiClientSecret string, -) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return err - } - importName := Config.GetWalletKey(key) - if err := wrappedClient.GenerateMounts(); err != nil { - return err - } - logger.Info(). - Str("provider", "gemini"). - Str("config-key", key). - Str("vault-key", importName). - Int("secret-length", len(geminiClientSecret)). - Msg("importing secret") - _, err = wrappedClient.ImportHmacSecret([]byte(geminiClientSecret), importName) - if err != nil { - return err - } - _, err = wrappedClient.Client.Logical().Write("wallets/"+importName, map[string]interface{}{ - "clientid": geminiClientID, - "clientkey": geminiClientKey, - }) - return err -} diff --git a/tools/vault/cmd/init.go b/tools/vault/cmd/init.go deleted file mode 100644 index c985d8f91..000000000 --- a/tools/vault/cmd/init.go +++ /dev/null @@ -1,200 +0,0 @@ -package vault - -import ( - "bytes" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "os" - "os/user" - "path" - - rootcmd "github.com/brave-intl/bat-go/cmd" - - cmdutils "github.com/brave-intl/bat-go/cmd" - - "github.com/brave-intl/bat-go/libs/closers" - appctx "github.com/brave-intl/bat-go/libs/context" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" - "github.com/hashicorp/vault/api" - "github.com/keybase/go-crypto/openpgp" - "github.com/keybase/go-crypto/openpgp/packet" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - // InitCmd initializes the vault server - InitCmd = &cobra.Command{ - Use: "init", - Short: "initializes the vault server", - Run: rootcmd.Perform("init vault", Initialize), - } -) - -func init() { - VaultCmd.AddCommand( - InitCmd, - ) - - initBuilder := cmdutils.NewFlagBuilder(InitCmd) - - // key-shares -> the number of shares to split the master key into: default 5 - initBuilder.Flag().Uint("key-shares", 5, - "the number of shares to split the master key into"). - Bind("key-shares") - - // key-threshold -> the number of shares needed to unseal: default 3 - initBuilder.Flag().Uint("key-threshold", 3, - "number of shares needed to unseal"). - Bind("key-threshold") -} - -// Initialize initializes the vault server -func Initialize(command *cobra.Command, args []string) error { - gpgKeyFiles := args - secretShares := viper.GetUint("key-shares") - secretThreshold := viper.GetUint("key-threshold") - logger, err := appctx.GetLogger(command.Context()) - cmdutils.Must(err) - - if len(gpgKeyFiles) == 0 { - return errors.New("a gpg file was not passed") - } else if len(gpgKeyFiles) != int(secretShares) { - return errors.New("a gpg public key file must be passed for every unseal share") - } - - var entityList openpgp.EntityList - gpgKeys := []string{} - - for i := 0; i < len(gpgKeyFiles); i++ { - f, err := os.Open(gpgKeyFiles[i]) - if err != nil { - return err - } - defer closers.Panic(command.Context(), f) - - // Vault only accepts keys in binary format, so we normalize the format - var entity openpgp.EntityList - - // Try to read the input file in armored format - entity, err = openpgp.ReadArmoredKeyRing(f) - if err != nil { - // On failure try to read it in binary format - _, err = f.Seek(0, 0) - if err != nil { - return err - } - entity, err = openpgp.ReadKeyRing(f) - if err != nil { - return err - } - } - if len(entity) > 1 { - return errors.New("your gpg public key files should only contain a single public key") - } - - buf := new(bytes.Buffer) - err = entity[0].Serialize(buf) - if err != nil { - return err - } - entityList = append(entityList, entity[0]) - gpgKeys = append(gpgKeys, base64.StdEncoding.EncodeToString(buf.Bytes())) - } - - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return err - } - - req := api.InitRequest{} - - req.PGPKeys = gpgKeys - req.SecretShares = int(secretShares) - req.SecretThreshold = int(secretThreshold) - - if secretShares > 1 && secretThreshold == 1 { - // Vault does not support this case but we can workaround and encrypt the single share to all keys - req.PGPKeys = []string{} - req.SecretShares = 1 - } - - resp, err := wrappedClient.Client.Sys().Init(&req) - if err != nil { - return err - } - - logger.Info().Msg("success, vault has been initialized") - - if secretShares > 1 && secretThreshold == 1 { - // We need to encrypt the single returned share to all keys ourselves - key := resp.Keys[0] - - logger.Info().Msgf("Writing share-0.gpg for all identities\n") - out, err := os.OpenFile("share-0.gpg", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - defer closers.Panic(command.Context(), out) - - encOut, err := openpgp.Encrypt(out, entityList, nil, &openpgp.FileHints{IsBinary: true}, nil) - if err != nil { - return err - } - defer closers.Panic(command.Context(), encOut) - - _, err = encOut.Write([]byte(key)) - if err != nil { - return err - } - } else { - // Vault has encrypted the shares for us - var b []byte - for i := range resp.KeysB64 { - b, err = base64.StdEncoding.DecodeString(resp.KeysB64[i]) - if err != nil { - return err - } - - // Parse the resulting encrypted files to print corresponding key for each - buf := bytes.NewBuffer(b) - packets := packet.NewReader(buf) - var p packet.Packet - for { - p, err = packets.Next() - if err != nil { - break - } - switch p := p.(type) { - case *packet.EncryptedKey: - keys := entityList.KeysById(p.KeyId, nil) - if len(keys) == 1 { - for k := range keys[0].Entity.Identities { - logger.Info().Msgf("Writing share-%d.gpg for %s\n", i, k) - } - } - } - } - - err = ioutil.WriteFile(fmt.Sprintf("share-%d.gpg", i), b, 0600) - if err != nil { - return err - } - } - } - - usr, err := user.Current() - if err != nil { - return err - } - - err = ioutil.WriteFile(path.Join(usr.HomeDir, ".vault-token"), []byte(resp.RootToken), 0600) - if err != nil { - return err - } - - logger.Info().Msg("Done! Note that the root token has been written to ~/.vault-token") - return nil -} diff --git a/tools/vault/cmd/sign_settlement.go b/tools/vault/cmd/sign_settlement.go deleted file mode 100644 index e9b54f6d3..000000000 --- a/tools/vault/cmd/sign_settlement.go +++ /dev/null @@ -1,516 +0,0 @@ -package vault - -import ( - "context" - "encoding/json" - "errors" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - rootcmd "github.com/brave-intl/bat-go/cmd" - - cmdutils "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/clients/bitflyer" - "github.com/brave-intl/bat-go/libs/clients/gemini" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/custodian" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - settlement "github.com/brave-intl/bat-go/tools/settlement" - bitflyersettlement "github.com/brave-intl/bat-go/tools/settlement/bitflyer" - settlementcmd "github.com/brave-intl/bat-go/tools/settlement/cmd" - geminisettlement "github.com/brave-intl/bat-go/tools/settlement/gemini" - upholdsettlement "github.com/brave-intl/bat-go/tools/settlement/uphold" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" - "github.com/shopspring/decimal" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - // SignSettlementCmd signs a settlement file's transactions - SignSettlementCmd = &cobra.Command{ - Use: "sign-settlement INPUT_FILE...", - Short: "sign settlement files using vault held secrets", - Run: rootcmd.Perform("sign settlement", SignSettlement), - } - // the combination of provider + transaction type gives you the key - // under which the vault secrets are located by default - providerTransactionTypes = map[string][]string{ - "uphold": {"contribution", "referral", "adsDirectDeposit"}, - "paypal": {"default"}, - "gemini": {"contribution", "referral", "adsDirectDeposit"}, - "bitflyer": {"default"}, - } - artifactGenerators = map[string]func( - context.Context, - string, - *vaultsigner.WrappedClient, - string, - []custodian.Transaction, - ) error{ - "uphold": createUpholdArtifact, - "gemini": createGeminiArtifact, - "paypal": createPaypalArtifact, - "bitflyer": createBitflyerArtifact, - } -) - -func init() { - VaultCmd.AddCommand( - SignSettlementCmd, - ) - - signSettlementBuilder := cmdutils.NewFlagBuilder(SignSettlementCmd) - - // in -> the file to parse and sign according to each provider's setup. default: contributions.json - signSettlementBuilder.Flag().String("in", "contributions.json", - "( legacy compatibility ) input file path"). - Bind("in") - - signSettlementBuilder.Flag().BoolP("merge-custodial", "m", false, "If present, combine multiple addresses which share a walletProviderID into a single transaction.") - - providers := []string{} - for k := range providerTransactionTypes { - providers = append(providers, k) - } - // providers -> the providers to parse out of the file and parse. default: uphold paypal gemini - signSettlementBuilder.Flag().StringSlice("providers", providers, - "providers to parse out of the given input files"). - Bind("providers") - - // jpyRate -> the providers to parse out of the file and parse. default: uphold paypal gemini - signSettlementBuilder.Flag().Float64("jpyrate", 0.0, - "jpyrate to use for paypal payouts"). - Bind("jpyrate") - - signSettlementBuilder.Flag().String("config", "config.yaml", - "the default path to a configuration file"). - Bind("config"). - Env("CONFIG") - - signSettlementBuilder.Flag().String("out", "", - "the output directory for prepared files ( defaults to current working directory )"). - Bind("out") - - signSettlementBuilder.Flag().Bool("merge", false, - "when multiple input files are passed, merge all transactions and output one file per provider / transaction type "). - Bind("merge") - - signSettlementBuilder.Flag().String("bitflyer-client-token", "", - "the token to be sent for auth on bitflyer"). - Bind("bitflyer-client-token"). - Env("BITFLYER_TOKEN") - - signSettlementBuilder.Flag().String("bitflyer-client-id", "", - "tells bitflyer what the client id is during token generation"). - Bind("bitflyer-client-id"). - Env("BITFLYER_CLIENT_ID") - - signSettlementBuilder.Flag().String("bitflyer-client-secret", "", - "tells bitflyer what the client secret during token generation"). - Bind("bitflyer-client-secret"). - Env("BITFLYER_CLIENT_SECRET") - - signSettlementBuilder.Flag().Bool("exclude-limited", false, - "in order to avoid not knowing what the payout amount will be because of transfer limits"). - Bind("exclude-limited") - - signSettlementBuilder.Flag().Int("chunk-size", 0, - "how many transfers to combine per request, 0 indicates the default value"). - Bind("chunk-size") -} - -// SignSettlement runs the signing of a settlement -func SignSettlement(command *cobra.Command, args []string) error { - inputFiles := args - - ReadConfig(command) - providers, err := command.Flags().GetStringSlice("providers") - if err != nil { - return err - } - inputFile, err := command.Flags().GetString("in") - if err != nil { - return err - } - if len(inputFiles) == 0 { - inputFiles = []string{inputFile} - } - outDir, err := command.Flags().GetString("out") - if err != nil { - return err - } - merge, err := command.Flags().GetBool("merge") - if err != nil { - return err - } - mergeCustodial, err := command.Flags().GetBool("merge-custodial") - if err != nil { - return err - } - - logger, err := appctx.GetLogger(command.Context()) - if err != nil { - return err - } - - if len(outDir) > 0 { - logger.Info().Str("outDir", outDir).Msg("creating output directory") - - err := os.MkdirAll(outDir, os.ModePerm) - if err != nil { - return err - } - } - - var mergedSettlements []settlement.AntifraudTransaction - - for _, inputFile := range inputFiles { - sublog := logger.With().Str("inputFile", inputFile).Logger() - - sublog.Info().Msg("reading settlement file") - - // append -signed to the filename - outBaseFile := strings.TrimSuffix(filepath.Base(inputFile), filepath.Ext(inputFile)) + "-signed.json" - - // all settlements file - settlementJSON, err := ioutil.ReadFile(inputFile) - if err != nil { - return err - } - - var antifraudSettlements []settlement.AntifraudTransaction - err = json.Unmarshal(settlementJSON, &antifraudSettlements) - if err != nil { - return err - } - - sublog.Info().Int("len(antifraudSettlements)", len(antifraudSettlements)).Msg("deserialized settlement file") - - mergedSettlements = append(mergedSettlements, antifraudSettlements...) - if merge { - sublog.Info().Int("len(mergedSettlements)", len(mergedSettlements)).Msg("merged settlements") - } else { - return processSettlements( - sublog.WithContext( - context.WithValue( - command.Context(), - appctx.MergeCustodialCTXKey, - mergeCustodial, - ), - ), - providers, - outDir, - outBaseFile, - antifraudSettlements, - ) - } - } - - err = settlement.CheckForDuplicates(mergedSettlements) - if err != nil { - return err - } - - if merge { - logger.Info().Int("len(mergedSettlements)", len(mergedSettlements)).Msg("processing merged settlements") - return processSettlements(command.Context(), providers, outDir, "merged-signed.json", mergedSettlements) - } - return nil -} - -func processSettlements(ctx context.Context, providers []string, outDir string, outBaseFile string, antifraudSettlements []settlement.AntifraudTransaction) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - return err - } - - settlementsByProviderAndWalletKey, err := divideSettlementsByWallet(ctx, antifraudSettlements) - if err != nil { - return err - } - - logLine := logger.Info() - for _, provider := range providers { - for _, txType := range providerTransactionTypes[provider] { - walletKey := provider + "-" + txType - logLine = logLine.Int(walletKey, len(settlementsByProviderAndWalletKey[walletKey])) - } - } - logLine.Msg("split settlements by provider and transaction type") - - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return err - } - - for _, provider := range providers { - for _, txType := range providerTransactionTypes[provider] { - walletKey := provider + "-" + txType - settlements := settlementsByProviderAndWalletKey[walletKey] - if len(settlements) == 0 { - continue - } - output := filepath.Join(outDir, walletKey+"-"+outBaseFile) - - secretKey := Config.GetWalletKey(walletKey) - - sublog := logger.With(). - Str("provider", provider). - Str("txType", txType). - Str("output", output). - Int("settlements", len(settlements)). - Logger() - - sublog.Info().Str("wallet", secretKey).Msg("attempting to sign settlements") - - err := artifactGenerators[provider]( - sublog.WithContext(ctx), - output, - wrappedClient, - secretKey, - settlements, - ) - - if err != nil { - return err - } - sublog.Info().Msg("created artifact") - } - } - return nil -} - -func divideSettlementsByWallet(ctx context.Context, antifraudTxs []settlement.AntifraudTransaction) (map[string][]custodian.Transaction, error) { - settlementTransactionsByWallet := make(map[string][]custodian.Transaction) - - logger, err := appctx.GetLogger(ctx) - if err != nil { - return settlementTransactionsByWallet, err - } - - for _, antifraudTx := range antifraudTxs { - tx, err := antifraudTx.ToTransaction() - if err != nil { - logger.Warn().Err(err).Str("channel", tx.Channel).Msg("skipping transaction as it failed to validate") - continue - } - - provider := tx.WalletProvider - wallet := tx.Type - if len(providerTransactionTypes[provider]) == 1 { - // might as well go into one (default) - wallet = providerTransactionTypes[provider][0] - } - - // which secret values to use to sign (paypal-default, uphold-referral, gemini-contribution) - walletKey := provider + "-" + wallet - // append to the nested structure - settlementTransactionsByWallet[walletKey] = append( - settlementTransactionsByWallet[walletKey], - tx, - ) - } - return settlementTransactionsByWallet, nil -} - -func createUpholdArtifact( - ctx context.Context, - outputFile string, - wrappedClient *vaultsigner.WrappedClient, - walletKey string, - upholdOnlySettlements []custodian.Transaction, -) error { - var transactionSet []custodian.Transaction = upholdOnlySettlements - response, err := wrappedClient.Client.Logical().Read("wallets/" + walletKey) - if err != nil { - return err - } - - providerID, ok := response.Data["providerId"] - if !ok { - return errors.New("invalid wallet name") - } - - // Ensure that there is only one payment for a given Uphold wallet by combining - // multiples if they exist. - mergeCustodialRaw := ctx.Value(appctx.MergeCustodialCTXKey) - mergeCustodial, ok := mergeCustodialRaw.(bool) - if ok && mergeCustodial == true { - transactionSet = upholdsettlement.FlattenPaymentsByWalletProviderID( - &upholdOnlySettlements, - ) - } - - signer, err := wrappedClient.GenerateEd25519Signer(walletKey) - if err != nil { - return err - } - - var info wallet.Info - info.PublicKey = signer.String() - info.Provider = "uphold" - info.ProviderID = providerID.(string) - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - settlementWallet := &uphold.Wallet{Info: info, PrivKey: signer, PubKey: signer} - - err = settlement.PrepareTransactions(settlementWallet, transactionSet, "payout", &uphold.Beneficiary{Relationship: "business"}) - if err != nil { - return err - } - - state := settlement.State{WalletInfo: settlementWallet.Info, Transactions: transactionSet} - - out, err := json.MarshalIndent(state, "", " ") - if err != nil { - return err - } - - err = ioutil.WriteFile(outputFile, out, 0400) - if err != nil { - return err - } - return nil -} - -// NewRefreshTokenPayloadFromViper creates the payload to refresh a bitflyer token from viper -func NewRefreshTokenPayloadFromViper() *bitflyer.TokenPayload { - vpr := viper.GetViper() - clientID := vpr.GetString("bitflyer-client-id") - clientSecret := vpr.GetString("bitflyer-client-secret") - extraClientSecret := vpr.GetString("bitflyer-extra-client-secret") - return &bitflyer.TokenPayload{ - GrantType: "client_credentials", - ClientID: clientID, - ClientSecret: clientSecret, - ExtraClientSecret: extraClientSecret, - } -} - -func createBitflyerArtifact( - ctx context.Context, - outputFile string, - wrappedClient *vaultsigner.WrappedClient, - walletKey string, - bitflyerOnlySettlements []custodian.Transaction, -) error { - bitflyerClient, err := bitflyer.New() - if err != nil { - return err - } - refreshTokenPayload := NewRefreshTokenPayloadFromViper() - _, err = bitflyerClient.RefreshToken(ctx, *refreshTokenPayload) - if err != nil { - return err - } - - vpr := viper.GetViper() - exclude := vpr.GetBool("exclude-limited") - sourceFrom := vpr.GetString("bitflyer-source-from") - - preparedTransactions, err := bitflyersettlement.PrepareRequests( - ctx, - bitflyerClient, - bitflyerOnlySettlements, - exclude, - sourceFrom, - ) - if err != nil { - return err - } - out, err := json.MarshalIndent(preparedTransactions, "", " ") - if err != nil { - return err - } - err = ioutil.WriteFile(outputFile, out, 0400) - if err != nil { - return err - } - return nil -} - -func createGeminiArtifact( - ctx context.Context, - outputFile string, - wrappedClient *vaultsigner.WrappedClient, - walletKey string, - geminiOnlySettlements []custodian.Transaction, -) error { - response, err := wrappedClient.Client.Logical().Read("wallets/" + walletKey) - if err != nil { - return err - } - oauthClientID := response.Data["clientid"].(string) - // group transactions (500 at a time) - privatePayloads, err := geminisettlement.TransformTransactions(ctx, oauthClientID, geminiOnlySettlements) - if err != nil { - return err - } - // leave enough space to increment nonce - <-time.After(time.Microsecond) - privateRequests, err := signGeminiRequests( - wrappedClient, - walletKey, - privatePayloads, - ) - if err != nil { - return err - } - // serialize requests to be sent in next step - out, err := json.MarshalIndent(privateRequests, "", " ") - if err != nil { - return err - } - err = ioutil.WriteFile(outputFile, out, 0400) - if err != nil { - return err - } - return nil -} - -func signGeminiRequests( - wrappedClient *vaultsigner.WrappedClient, - walletKey string, - privateRequests *[][]gemini.PayoutPayload, -) (*[]gemini.PrivateRequestSequence, error) { - response, err := wrappedClient.Client.Logical().Read("wallets/" + walletKey) - if err != nil { - return nil, err - } - clientID := response.Data["clientid"].(string) - clientKey := response.Data["clientkey"].(string) - hmacSecret, err := wrappedClient.GetHmacSecret(walletKey) - if err != nil { - return nil, err - } - return geminisettlement.SignRequests( - clientID, - clientKey, - hmacSecret, - privateRequests, - ) -} - -func createPaypalArtifact( - ctx context.Context, - outputFile string, - client *vaultsigner.WrappedClient, - walletKey string, - paypalOnlySettlements []custodian.Transaction, -) error { - return settlementcmd.PaypalTransformForMassPay( - ctx, - &paypalOnlySettlements, - "JPY", - decimal.NewFromFloat(viper.GetFloat64("jpyrate")), - outputFile, - ) -} diff --git a/tools/vault/cmd/unseal.go b/tools/vault/cmd/unseal.go deleted file mode 100644 index 94097b3e3..000000000 --- a/tools/vault/cmd/unseal.go +++ /dev/null @@ -1,84 +0,0 @@ -package vault - -import ( - "bufio" - "fmt" - "io/ioutil" - "os" - - rootcmd "github.com/brave-intl/bat-go/cmd" - - appctx "github.com/brave-intl/bat-go/libs/context" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" - "github.com/spf13/cobra" - "golang.org/x/term" -) - -var ( - // UnsealCmd initializes the vault server - UnsealCmd = &cobra.Command{ - Use: "unseal", - Short: "unseals the vault server", - Run: rootcmd.Perform("unseal vault", Unseal), - } -) - -func init() { - VaultCmd.AddCommand( - UnsealCmd, - ) -} - -// Unseal unseals the vault to allow for insertions -func Unseal(command *cobra.Command, args []string) error { - logger, err := appctx.GetLogger(command.Context()) - if err != nil { - return err - } - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return err - } - - fi, err := os.Stdin.Stat() - if err != nil { - return err - } - - var b []byte - - if (fi.Mode() & os.ModeNamedPipe) == 0 { - fmt.Print("Please enter your unseal key: ") - b, err = term.ReadPassword(int(os.Stdin.Fd())) - } else { - reader := bufio.NewReader(os.Stdin) - b, err = ioutil.ReadAll(reader) - } - if err != nil { - return err - } - - status, err := wrappedClient.Client.Sys().Unseal(string(b)) - if err != nil { - return err - } - - t := status.T - sealed := status.Sealed - logEvent := logger.Info(). - Str("Seal Type", status.Type). - Bool("Sealed", sealed). - Int("Total Shares", status.N). - Int("Threshold", t) - - if sealed { - logEvent = logEvent.Int("progress", status.Progress). - Int("total", t). - Str("nonce", status.Nonce) - } - logEvent.Send() - if err != nil { - return err - } - return nil -} diff --git a/tools/vault/cmd/vault.go b/tools/vault/cmd/vault.go deleted file mode 100644 index 700ad3940..000000000 --- a/tools/vault/cmd/vault.go +++ /dev/null @@ -1,33 +0,0 @@ -package vault - -import ( - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - settlement "github.com/brave-intl/bat-go/tools/settlement" - "github.com/spf13/cobra" -) - -var ( - // Config is a configuration file to map known wallet keys to unknown wallet keys - Config *settlement.Config - - // VaultCmd adds a command to cobra for vault interfacing - VaultCmd = &cobra.Command{ - Use: "vault", - Short: "provides a succinct interface with vault", - } -) - -func init() { - rootcmd.RootCmd.AddCommand(VaultCmd) -} - -// ReadConfig sets up the config flag -func ReadConfig(command *cobra.Command) *settlement.Config { - configPath, err := command.Flags().GetString("config") - cmdutils.Must(err) - config, err := settlement.ReadYamlConfig(configPath) - cmdutils.Must(err) - Config = config - return config -} diff --git a/tools/vault/signer/ed25519signer.go b/tools/vault/signer/ed25519signer.go deleted file mode 100644 index d03f5a976..000000000 --- a/tools/vault/signer/ed25519signer.go +++ /dev/null @@ -1,71 +0,0 @@ -package vaultsigner - -import ( - "crypto" - "encoding/base64" - "encoding/hex" - "fmt" - "io" - "strconv" - "strings" - - "github.com/hashicorp/vault/api" - "golang.org/x/crypto/ed25519" -) - -// Ed25519Signer signer / verifier that uses the vault transit backend -type Ed25519Signer struct { - Client *api.Client - KeyName string - KeyVersion uint -} - -// Sign the included message using the vault held keypair. rand and opts are not used -func (vs *Ed25519Signer) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) { - response, err := vs.Client.Logical().Write("transit/sign/"+vs.KeyName, map[string]interface{}{ - "input": base64.StdEncoding.EncodeToString(message), - }) - if err != nil { - return []byte{}, err - } - - sig := response.Data["signature"].(string) - - return base64.StdEncoding.DecodeString(strings.Split(sig, ":")[2]) -} - -// Verify the included signature over message using the vault held keypair. opts are not used -func (vs *Ed25519Signer) Verify(message, signature []byte, opts crypto.SignerOpts) (bool, error) { - response, err := vs.Client.Logical().Write("transit/verify/"+vs.KeyName, map[string]interface{}{ - "input": base64.StdEncoding.EncodeToString(message), - "signature": fmt.Sprintf("vault:v%d:%s", vs.KeyVersion, base64.StdEncoding.EncodeToString(signature)), - }) - if err != nil { - return false, err - } - - return response.Data["valid"].(bool), nil -} - -// String returns the public key as a hex encoded string -func (vs *Ed25519Signer) String() string { - return hex.EncodeToString(vs.Public().(ed25519.PublicKey)) -} - -// Public returns the public key -func (vs *Ed25519Signer) Public() crypto.PublicKey { - response, err := vs.Client.Logical().Read("transit/keys/" + vs.KeyName) - if err != nil { - panic(err) - } - - keys := response.Data["keys"].(map[string]interface{}) - key := keys[strconv.Itoa(int(vs.KeyVersion))].(map[string]interface{}) - b64PublicKey := key["public_key"].(string) - publicKey, err := base64.StdEncoding.DecodeString(b64PublicKey) - if err != nil { - panic(err) - } - - return ed25519.PublicKey(publicKey) -} diff --git a/tools/vault/signer/hmacsigner.go b/tools/vault/signer/hmacsigner.go deleted file mode 100644 index 99dfd8d58..000000000 --- a/tools/vault/signer/hmacsigner.go +++ /dev/null @@ -1,29 +0,0 @@ -package vaultsigner - -import ( - "encoding/base64" - "strings" - - "github.com/hashicorp/vault/api" -) - -// HmacSigner signer / verifier that uses the vault transit backend -type HmacSigner struct { - Client *api.Client - KeyName string - KeyVersion uint -} - -// HMACSha384 the included message using the vault held keypair -func (vs *HmacSigner) HMACSha384(message []byte) ([]byte, error) { - response, err := vs.Client.Logical().Write("transit/hmac/"+vs.KeyName+"/sha2-384", map[string]interface{}{ - "input": base64.StdEncoding.EncodeToString(message), - }) - if err != nil { - return []byte{}, err - } - - hmac := response.Data["hmac"].(string) - - return base64.StdEncoding.DecodeString(strings.Split(hmac, ":")[2]) -} diff --git a/tools/vault/signer/hmacsigner_test.go b/tools/vault/signer/hmacsigner_test.go deleted file mode 100644 index d12a037f4..000000000 --- a/tools/vault/signer/hmacsigner_test.go +++ /dev/null @@ -1,51 +0,0 @@ -//go:build integration -// +build integration - -package vaultsigner - -import ( - "encoding/hex" - "testing" - - "github.com/brave-intl/bat-go/libs/cryptography" - uuid "github.com/satori/go.uuid" -) - -func TestHmacSign(t *testing.T) { - wrappedClient, err := Connect() - if err != nil { - t.Fatal(err) - } - - name := "vaultsigner-hmactest-" + uuid.NewV4().String() - - secret := []byte("mysecret") - data := []byte("hello world") - - // Create a new HMAC by defining the hash type and the key (as byte array) - h := cryptography.NewHMACHasher(secret) - inMemorySha, err := h.HMACSha384(data) - if err != nil { - t.Fatal(err) - } - hexInMemorySha := hex.EncodeToString(inMemorySha) - - _, err = wrappedClient.ImportHmacSecret(secret, name) - if err != nil { - t.Fatal(err) - } - signer, err := wrappedClient.GetHmacSecret(name) - if err != nil { - t.Fatal(err) - } - - vaultSha, err := signer.HMACSha384(data) - if err != nil { - t.Fatal(err) - } - hexVaultSha := hex.EncodeToString(vaultSha) - - if hexVaultSha != hexInMemorySha { - t.Fatalf("shas did not match:\n%s\n%s", hexVaultSha, hexInMemorySha) - } -} diff --git a/tools/vault/signer/vaultsigner.go b/tools/vault/signer/vaultsigner.go deleted file mode 100644 index 18e72a303..000000000 --- a/tools/vault/signer/vaultsigner.go +++ /dev/null @@ -1,227 +0,0 @@ -package vaultsigner - -import ( - "crypto/rand" - "encoding/base64" - "time" - - "github.com/hashicorp/vault/api" - util "github.com/hashicorp/vault/command/config" - "github.com/hashicorp/vault/sdk/helper/jsonutil" - "github.com/hashicorp/vault/sdk/helper/keysutil" - "golang.org/x/crypto/ed25519" -) - -// WrappedClient holds an api client for interacting with vault -type WrappedClient struct { - Client *api.Client -} - -// FromKeypair create a new vault transit key by importing privKey and pubKey under importName -func (wc *WrappedClient) FromKeypair(privKey ed25519.PrivateKey, pubKey ed25519.PublicKey, importName string) (*Ed25519Signer, error) { - client := wc.Client - key := keysutil.KeyEntry{} - - key.Key = privKey - - pk := base64.StdEncoding.EncodeToString(pubKey) - key.FormattedPublicKey = pk - - { - tmp := make([]byte, 32) - _, err := rand.Read(tmp) - if err != nil { - return nil, err - } - key.HMACKey = tmp - } - - key.CreationTime = time.Now().UTC() - key.DeprecatedCreationTime = key.CreationTime.Unix() - - keyData := keysutil.KeyData{Policy: &keysutil.Policy{Keys: map[string]keysutil.KeyEntry{"1": key}}} - - keyData.Policy.ArchiveVersion = 1 - keyData.Policy.BackupInfo = &keysutil.BackupInfo{Time: time.Now().UTC(), Version: 1} - keyData.Policy.LatestVersion = 1 - keyData.Policy.MinDecryptionVersion = 1 - keyData.Policy.Name = importName - keyData.Policy.Type = keysutil.KeyType_ED25519 - - encodedBackup, err := jsonutil.EncodeJSON(keyData) - if err != nil { - return nil, err - } - backup := base64.StdEncoding.EncodeToString(encodedBackup) - - err = wc.GenerateMounts() - if err != nil { - return nil, err - } - - // Restore the generated key backup - _, err = client.Logical().Write("transit/restore", map[string]interface{}{ - "backup": backup, - "name": importName, - }) - if err != nil { - return nil, err - } - - return &Ed25519Signer{Client: client, KeyName: importName, KeyVersion: 1}, nil -} - -// ImportHmacSecret create a new vault transit key by importing privKey under importName -func (wc *WrappedClient) ImportHmacSecret(secret []byte, importName string) (*HmacSigner, error) { - client := wc.Client - key := keysutil.KeyEntry{} - - key.HMACKey = secret - key.Key = secret - - key.CreationTime = time.Now().UTC() - key.DeprecatedCreationTime = key.CreationTime.Unix() - - keyData := keysutil.KeyData{Policy: &keysutil.Policy{Keys: map[string]keysutil.KeyEntry{"1": key}}} - - keyData.Policy.ArchiveVersion = 1 - keyData.Policy.BackupInfo = &keysutil.BackupInfo{Time: time.Now().UTC(), Version: 1} - keyData.Policy.LatestVersion = 1 - keyData.Policy.MinDecryptionVersion = 1 - keyData.Policy.Name = importName - keyData.Policy.Exportable = true - keyData.Policy.AllowPlaintextBackup = true - - encodedBackup, err := jsonutil.EncodeJSON(keyData) - if err != nil { - return nil, err - } - backup := base64.StdEncoding.EncodeToString(encodedBackup) - - err = wc.GenerateMounts() - if err != nil { - return nil, err - } - - // Restore the generated key backup - _, err = client.Logical().Write("transit/restore", map[string]interface{}{ - "backup": backup, - "name": importName, - }) - if err != nil { - return nil, err - } - - return &HmacSigner{Client: client, KeyName: importName, KeyVersion: 1}, nil -} - -// GenerateMounts generates the appropriate mount points if they do not exist -func (wc *WrappedClient) GenerateMounts() error { - mounts, err := wc.Client.Sys().ListMounts() - if err != nil { - return err - } - if _, ok := mounts["wallets/"]; !ok { - // Mount kv secret backend if not already mounted - if err = wc.Client.Sys().Mount("wallets", &api.MountInput{ - Type: "kv", - }); err != nil { - return err - } - } - if _, ok := mounts["transit/"]; !ok { - // Mount transit secret backend if not already mounted - if err = wc.Client.Sys().Mount("transit", &api.MountInput{ - Type: "transit", - }); err != nil { - return err - } - } - return nil -} - -// GenerateEd25519Signer create Ed25519Signer by generating a keypair with name using vault backend -func (wc *WrappedClient) GenerateEd25519Signer(name string) (*Ed25519Signer, error) { - err := wc.GenerateMounts() - if err != nil { - return nil, err - } - // Generate a new keypair - _, err = wc.Client.Logical().Write("transit/keys/"+name, map[string]interface{}{ - "type": "ed25519", - }) - if err != nil { - return nil, err - } - - return &Ed25519Signer{Client: wc.Client, KeyName: name, KeyVersion: 1}, nil -} - -// GetEd25519Signer gets a key pair but doesn't generate new key -func (wc *WrappedClient) GetEd25519Signer(name string) (*Ed25519Signer, error) { - err := wc.GenerateMounts() - if err != nil { - return nil, err - } - - return &Ed25519Signer{Client: wc.Client, KeyName: name, KeyVersion: 1}, nil -} - -// GenerateHmacSecret create hmac key using vault backend -func (wc *WrappedClient) GenerateHmacSecret(name string, algo string) (*HmacSigner, error) { - err := wc.GenerateMounts() - if err != nil { - return nil, err - } - - // Generate a new hmac set - _, err = wc.Client.Logical().Write("transit/hmac/"+name+"/"+algo, nil) - if err != nil { - return nil, err - } - - return &HmacSigner{Client: wc.Client, KeyName: name, KeyVersion: 1}, nil -} - -// GetHmacSecret gets a key pair but doesn't generate new key -func (wc *WrappedClient) GetHmacSecret(name string) (*HmacSigner, error) { - err := wc.GenerateMounts() - if err != nil { - return nil, err - } - - return &HmacSigner{Client: wc.Client, KeyName: name, KeyVersion: 1}, nil -} - -// Connect connects to the vaultsigner backend server, sets token written by vault -func Connect() (*WrappedClient, error) { - var client *api.Client - config := &api.Config{} - err := config.ReadEnvironment() - - if err == nil { - client, err = api.NewClient(config) - } else { - client, err = api.NewClient(nil) - if err != nil { - return nil, err - } - err = client.SetAddress("http://127.0.0.1:8200") - } - if err != nil { - return nil, err - } - - helper, err := util.DefaultTokenHelper() - if err == nil { - var token string - token, err = helper.Get() - if err == nil { - if token != "" { - client.SetToken(token) - } - } - } - - return &WrappedClient{Client: client}, err -} diff --git a/tools/vault/signer/vaultsigner_test.go b/tools/vault/signer/vaultsigner_test.go deleted file mode 100644 index 7e5b22f34..000000000 --- a/tools/vault/signer/vaultsigner_test.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build integration -// +build integration - -package vaultsigner - -import ( - "crypto" - "crypto/rand" - "testing" - - uuid "github.com/satori/go.uuid" - "golang.org/x/crypto/ed25519" -) - -func TestSign(t *testing.T) { - wrappedClient, err := Connect() - if err != nil { - t.Fatal(err) - } - - publicKey, privateKey, err := ed25519.GenerateKey(nil) - if err != nil { - t.Fatal(err) - } - name := uuid.NewV4() - - signer, err := wrappedClient.FromKeypair(privateKey, publicKey, "vaultsigner-test-"+name.String()) - if err != nil { - t.Fatal(err) - } - - message := []byte("hello world") - - signature, err := signer.Sign(rand.Reader, message, crypto.Hash(0)) - if err != nil { - t.Fatal(err) - } - - if !ed25519.Verify(publicKey, message, signature) { - t.Fatal("Signature did not match") - } -} - -func TestVerify(t *testing.T) { - wrappedClient, err := Connect() - if err != nil { - t.Fatal(err) - } - - publicKey, privateKey, err := ed25519.GenerateKey(nil) - if err != nil { - t.Fatal(err) - } - name := uuid.NewV4() - if err != nil { - t.Fatal(err) - } - - signer, err := wrappedClient.FromKeypair(privateKey, publicKey, "vaultsigner-test-"+name.String()) - if err != nil { - t.Fatal(err) - } - - message := []byte("hello world") - - signature, err := privateKey.Sign(rand.Reader, message, crypto.Hash(0)) - if err != nil { - t.Fatal(err) - } - - valid, err := signer.Verify(message, signature, crypto.Hash(0)) - if err != nil { - t.Fatal(err) - } - if !valid { - t.Fatal("Signature should be valid") - } -} diff --git a/tools/wallet/cmd/create.go b/tools/wallet/cmd/create.go deleted file mode 100644 index 97ccd5663..000000000 --- a/tools/wallet/cmd/create.go +++ /dev/null @@ -1,105 +0,0 @@ -package cmd - -import ( - "context" - "encoding/hex" - "fmt" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/altcurrency" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - "github.com/spf13/cobra" -) - -var ( - // CreateCmd creates a wallet on uphold - CreateCmd = &cobra.Command{ - Use: "create", - Short: "creates a wallet", - Run: rootcmd.Perform("create", Create), - } -) - -func init() { - WalletsCmd.AddCommand(CreateCmd) - - createBuilder := cmdutils.NewFlagBuilder(CreateCmd) - - // name - the name of the new wallet - createBuilder.Flag().String("name", "", - "the name for the wallet"). - Bind("name") - - // provider - the provider to target - createBuilder.Flag().String("provider", "", - "the provider for the wallet"). - Bind("provider") -} - -// Create creates a wallet -func Create(cmd *cobra.Command, args []string) error { - provider, err := cmd.Flags().GetString("provider") - fmt.Println("provider", err) - if err != nil { - return err - } - switch provider { - case "uphold": - name, err := cmd.Flags().GetString("name") - if err != nil { - return err - } - return CreateOnUphold( - cmd.Context(), - name, - ) - } - return nil -} - -// CreateOnUphold creates a wallet on uphold -func CreateOnUphold(ctx context.Context, name string) error { - logger, lerr := appctx.GetLogger(ctx) - if lerr != nil { - _, logger = logging.SetupLogger(ctx) - } - - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) - if err != nil { - return err - } - publicKeyHex := hex.EncodeToString([]byte(publicKey)) - - privateKeyHex := hex.EncodeToString([]byte(privateKey)) - logger.Info(). - Str("public_key", publicKeyHex). - Str("private_key", privateKeyHex). - Str("name", name). - Msg("key created") - - var info wallet.Info - info.Provider = "uphold" - info.ProviderID = "" - { - tmp := altcurrency.BAT - info.AltCurrency = &tmp - } - info.PublicKey = publicKeyHex - - wallet := &uphold.Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} - - err = wallet.Register(ctx, name) - if err != nil { - return err - } - - logger.Info(). - Str("provider_id", wallet.Info.ProviderID). - Msg("Uphold card ID") - return nil -} diff --git a/tools/wallet/cmd/list_transactions.go b/tools/wallet/cmd/list_transactions.go deleted file mode 100644 index ced084cc6..000000000 --- a/tools/wallet/cmd/list_transactions.go +++ /dev/null @@ -1,190 +0,0 @@ -package cmd - -import ( - "context" - "encoding/csv" - "flag" - "fmt" - "os" - "sort" - "time" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/altcurrency" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider" - "github.com/spf13/cobra" -) - -const ( - dateFormat = "2006-01-02T15:04:05-0700" -) - -// var csvOut = flag.Bool("csv", false, "csv output") -// var limit = flag.Int("limit", 50, "limit number of transactions returned") -// var startDateStr = flag.String("start-date", "none", "only include transactions after this datetime [ISO 8601]") -// var walletProvider = flag.String("provider", "uphold", "provider for the source wallet") -// var signed = flag.Bool("signed", false, "signed value depending on transaction direction") - -var ( - // ListTransactionsCmd is a command to list transactions from a given wallet - ListTransactionsCmd = &cobra.Command{ - Use: "list-transactions", - Short: "lists a transactions from a given wallet", - Run: rootcmd.Perform("list transactions", RunListTransactions), - } -) - -func init() { - WalletsCmd.AddCommand(ListTransactionsCmd) - - listTransactionsBuilder := cmdutils.NewFlagBuilder(ListTransactionsCmd) - - listTransactionsBuilder.Flag().Bool("csv", false, - "the output file should be csv"). - Bind("csv"). - Require() - - listTransactionsBuilder.Flag().Bool("signed", false, - "signed value depending on transaction direction"). - Bind("signed"). - Require() - - listTransactionsBuilder.Flag().Int("limit", 50, - "limit number of transactions returned"). - Bind("limit"). - Require() - - listTransactionsBuilder.Flag().String("start-date", "none", - "only include transactions after this datetime [ISO 8601]"). - Bind("start-date"). - Require() - - listTransactionsBuilder.Flag().String("provider", "uphold", - "provider for the source wallet"). - Bind("provider"). - Require() -} - -// RunListTransactions runs the list transactions command -func RunListTransactions(cmd *cobra.Command, args []string) error { - csvOut, err := cmd.Flags().GetBool("csv") - if err != nil { - return err - } - signed, err := cmd.Flags().GetBool("signed") - if err != nil { - return err - } - limit, err := cmd.Flags().GetInt("limit") - if err != nil { - return err - } - startDateStr, err := cmd.Flags().GetString("start-date") - if err != nil { - return err - } - provider, err := cmd.Flags().GetString("provider") - if err != nil { - return err - } - return ListTransactions( - cmd.Context(), - args, - csvOut, - signed, - limit, - startDateStr, - provider, - ) -} - -// ListTransactions lists transactions -func ListTransactions( - ctx context.Context, - args []string, - csvOut bool, - signed bool, - limit int, - startDateStr string, - walletProvider string, -) error { - var err error - startDate := time.Unix(0, 0) - if startDateStr != "none" { - startDate, err = time.Parse(dateFormat, startDateStr) - if err != nil { - return fmt.Errorf("%s is not a valid ISO 8601 datetime", startDateStr) - } - } - - walletc := altcurrency.BAT - info := wallet.Info{ - Provider: walletProvider, - ProviderID: flag.Args()[0], - AltCurrency: &walletc, - } - w, err := provider.GetWallet(ctx, info) - if err != nil { - return err - } - - txns, err := w.ListTransactions(ctx, limit, startDate) - if err != nil { - return err - } - - sort.Sort(wallet.ByTime(txns)) - - if csvOut { - w := csv.NewWriter(os.Stdout) - err = w.Write([]string{"id", "date", "description", "probi", "altcurrency", "source", "destination", "transferFee", "exchangeFee", "destAmount", "destCurrency"}) - if err != nil { - return err - } - - for i := 0; i < len(txns); i++ { - t := txns[i] - - value := t.AltCurrency.FromProbi(t.Probi).String() - if signed { - if t.Source == info.ProviderID { - value = "-" + value - } else if t.Destination == info.ProviderID { - value = "+" + value - } else { - panic("Could not determine direction of transaction") - } - } - - record := []string{ - t.ID, - t.Time.String(), - t.Note, - value, - t.AltCurrency.String(), - t.Source, - t.Destination, - t.TransferFee.String(), - t.ExchangeFee.String(), - t.DestAmount.String(), - t.DestCurrency, - } - if err := w.Write(record); err != nil { - return fmt.Errorf("error writing record to csv: %s", err) - } - } - - w.Flush() - - if err := w.Error(); err != nil { - return err - } - } else { - for i := 0; i < len(txns); i++ { - fmt.Printf("%s\n", txns[i]) - } - } - return nil -} diff --git a/tools/wallet/cmd/transfer_funds.go b/tools/wallet/cmd/transfer_funds.go deleted file mode 100644 index 8ff8ddc02..000000000 --- a/tools/wallet/cmd/transfer_funds.go +++ /dev/null @@ -1,366 +0,0 @@ -package cmd - -import ( - "bufio" - "context" - "crypto" - "encoding/hex" - "encoding/json" - "errors" - "os" - - cmdutils "github.com/brave-intl/bat-go/cmd" - rootcmd "github.com/brave-intl/bat-go/cmd" - "github.com/brave-intl/bat-go/libs/altcurrency" - appctx "github.com/brave-intl/bat-go/libs/context" - "github.com/brave-intl/bat-go/libs/httpsignature" - "github.com/brave-intl/bat-go/libs/logging" - "github.com/brave-intl/bat-go/libs/passphrase" - "github.com/brave-intl/bat-go/libs/prompt" - "github.com/brave-intl/bat-go/libs/wallet" - "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" - vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" - "github.com/shopspring/decimal" - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "golang.org/x/crypto/ed25519" -) - -var ( - // TransferFundsCmd transfer funds command - TransferFundsCmd = &cobra.Command{ - Use: "transfer-funds", - Short: "transfers funds from one wallet to another", - Run: rootcmd.Perform("transfer funds", RunTransferFunds), - } - // WalletsCmd root wallets command - WalletsCmd = &cobra.Command{ - Use: "wallet", - Short: "provides wallets micro-service entrypoint", - } -) - -func init() { - - // add this command as a serve subcommand - rootcmd.RootCmd.AddCommand(WalletsCmd) - - WalletsCmd.AddCommand( - TransferFundsCmd, - ) - - transferFundsBuilder := cmdutils.NewFlagBuilder(TransferFundsCmd) - transferFundsBuilder.Flag().String("currency", "BAT", - "currency for transfer"). - Bind("currency") - - transferFundsBuilder.Flag().String("from", "", - "vault name for the source wallet"). - Bind("from"). - Require() - - transferFundsBuilder.Flag().String("note", "", - "optional note for the transfer"). - Bind("note") - - transferFundsBuilder.Flag().String("purpose", "", - "purpose for the transfer, required for value > $3000"). - Bind("purpose") - - transferFundsBuilder.Flag().String("beneficiary", "", - "JSON formatted beneficiary for the transfer, required for value > $3000"). - Bind("beneficiary") - - transferFundsBuilder.Flag().Bool("oneshot", false, - "submit and commit without confirming"). - Bind("oneshot") - - transferFundsBuilder.Flag().String("to", "", - "destination wallet address"). - Bind("to"). - Require() - - transferFundsBuilder.Flag().String("value", "", - "amount to transfer [float or all]"). - Bind("value"). - Require() - - transferFundsBuilder.Flag().String("provider", "uphold", - "provider for the source wallet"). - Bind("provider") - - transferFundsBuilder.Flag().Bool("usevault", false, - "should signer should pull from vault"). - Bind("usevault") -} - -// RunTransferFunds moves funds from one wallet to another -func RunTransferFunds(command *cobra.Command, args []string) error { - value, err := command.Flags().GetString("value") - if err != nil { - return err - } - from, err := command.Flags().GetString("from") - if err != nil { - return err - } - to, err := command.Flags().GetString("to") - if err != nil { - return err - } - currency, err := command.Flags().GetString("currency") - if err != nil { - return err - } - note, err := command.Flags().GetString("note") - if err != nil { - return err - } - purpose, err := command.Flags().GetString("purpose") - if err != nil { - return err - } - beneficiaryJSON, err := command.Flags().GetString("beneficiary") - if err != nil { - return err - } - var beneficiary *uphold.Beneficiary - if len(beneficiaryJSON) > 0 { - beneficiary = &uphold.Beneficiary{} - err := json.Unmarshal([]byte(beneficiaryJSON), beneficiary) - if err != nil { - return err - } - } - oneshot, err := command.Flags().GetBool("oneshot") - if err != nil { - return err - } - usevault, err := command.Flags().GetBool("usevault") - if err != nil { - return err - } - - ctx := command.Context() - return TransferFunds( - ctx, - from, - to, - value, - currency, - note, - purpose, - beneficiary, - oneshot, - usevault, - ) -} - -func pullRequisiteSecrets(from string, usevault bool) (string, crypto.Signer, error) { - if usevault { - return pullRequisiteSecretsFromVault(from) - } - providerID, privateKey, err := pullRequisiteSecretsFromEnv(from) - if privateKey == nil { - // Fallback to prompting for a seed phrase - return pullRequisiteSecretsFromPrompt(from) - } - return providerID, privateKey, err -} - -func pullRequisiteSecretsFromPrompt(from string) (string, crypto.Signer, error) { - log.Println("Enter your recovery phrase:") - reader := bufio.NewReader(os.Stdin) - recoveryPhrase, err := reader.ReadString('\n') - if err != nil { - return "", nil, err - } - - seed, err := passphrase.ToBytes32(recoveryPhrase) - if err != nil { - return "", nil, err - } - - key, err := passphrase.DeriveSigningKeysFromSeed(seed, passphrase.LedgerHKDFSalt) - if err != nil { - return "", nil, err - } - - return from, key, nil -} - -func pullRequisiteSecretsFromEnv(from string) (string, crypto.Signer, error) { - privateKeyHex := os.Getenv("ED25519_PRIVATE_KEY") - - if len(privateKeyHex) == 0 { - return "", nil, nil - } - - var privKey ed25519.PrivateKey - privKey, err := hex.DecodeString(privateKeyHex) - if err != nil { - return "", nil, errors.New("Key material must be passed as hex") - } - - return from, privKey, nil -} - -func pullRequisiteSecretsFromVault(from string) (string, *vaultsigner.Ed25519Signer, error) { - wrappedClient, err := vaultsigner.Connect() - if err != nil { - return "", nil, err - } - - response, err := wrappedClient.Client.Logical().Read("wallets/" + from) - if err != nil { - return "", nil, err - } - - providerID, ok := response.Data["providerId"] - if !ok { - return "", nil, errors.New("invalid wallet name") - } - - signer, err := wrappedClient.GenerateEd25519Signer(from) - if err != nil { - return "", signer, err - } - - providerIDString := providerID.(string) - return providerIDString, signer, nil -} - -// TransferFunds transfers funds to a wallet -func TransferFunds( - ctx context.Context, - from string, - to string, - value string, - currency string, - note string, - purpose string, - beneficiary *uphold.Beneficiary, - oneshot bool, - usevault bool, -) error { - logger, err := appctx.GetLogger(ctx) - if err != nil { - _, logger = logging.SetupLogger(ctx) - } - logger.Debug().Msg("debug enabled") - valueDec, err := decimal.NewFromString(value) - if value != "all" && (err != nil || valueDec.LessThanOrEqual(decimal.Zero)) { - return errors.New("must pass --value greater than 0 or --value=all") - } - - providerID, signer, err := pullRequisiteSecrets(from, usevault) - if err != nil { - return err - } - walletc := altcurrency.BAT - - var info wallet.Info - info.PublicKey = hex.EncodeToString(signer.Public().(ed25519.PublicKey)) - info.Provider = "uphold" - info.ProviderID = providerID - { - tmp := walletc - info.AltCurrency = &tmp - } - - var pubKey httpsignature.Ed25519PubKey - pubKey, err = hex.DecodeString(info.PublicKey) - if err != nil { - return err - } - - w, err := uphold.New(ctx, info, signer, pubKey) - if err != nil { - return err - } - - altc, err := altcurrency.FromString(currency) - if err != nil { - return err - } - - var valueProbi decimal.Decimal - var balance *wallet.Balance - - if walletc == altc { - balance, err = w.GetBalance(ctx, true) - if err != nil { - return err - } - } - - if value == "all" { - if walletc == altc { - valueProbi = balance.SpendableProbi - } else { - return errors.New("sending all funds not available for currencies other than the wallet currency") - } - } else { - valueProbi = altc.ToProbi(valueDec) - if walletc == altc && balance.SpendableProbi.LessThan(valueProbi) { - return errors.New("insufficient funds in wallet") - } - } - - signedTx, err := w.PrepareTransaction(altc, valueProbi, to, note, purpose, beneficiary) - if err != nil { - return err - } - for { - submitInfo, err := w.SubmitTransaction(ctx, signedTx, oneshot) - if err != nil { - return err - } - if oneshot { - logger.Info().Msg("transfer complete") - break - } - - logger.Info(). - Str("id", submitInfo.ID). - Str("from", from). - Str("to", to). - Str("currency", currency). - Str("amount", altc.FromProbi(valueProbi).String()). - Msg("will transfer") - - log.Printf("Continue? ") - resp, err := prompt.Bool() - if err != nil { - return err - } - if !resp { - return errors.New("exiting") - } - - _, err = w.ConfirmTransaction(ctx, submitInfo.ID) - if err != nil { - logger.Error().Err(err).Msg("error confirming") - return err - } - - upholdInfo, err := w.GetTransaction(ctx, submitInfo.ID) - if err != nil { - return err - } - if upholdInfo.Status == "completed" { - logger.Info().Msg("transfer complete") - break - } - - log.Printf("Confirmation did not appear to go through, retry?") - resp, err = prompt.Bool() - if err != nil { - return err - } - if !resp { - return errors.New("exiting") - } - } - return nil -}