diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100755 index 00000000..7dbdf15e --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,541 @@ +title = "gitleaks config" + +# Gitleaks rules are defined by regular expressions and entropy ranges. +# Some secrets have unique signatures which make detecting those secrets easy. +# Examples of those secrets would be Gitlab Personal Access Tokens, AWS keys, and Github Access Tokens. +# All these examples have defined prefixes like `glpat`, `AKIA`, `ghp_`, etc. +# +# Other secrets might just be a hash which means we need to write more complex rules to verify +# that what we are matching is a secret. +# +# Here is an example of a semi-generic secret +# +# discord_client_secret = "8dyfuiRyq=vVc3RRr_edRk-fK__JItpZ" +# +# We can write a regular expression to capture the variable name (identifier), +# the assignment symbol (like '=' or ':='), and finally the actual secret. +# The structure of a rule to match this example secret is below: +# +# Beginning string +# quotation +# │ End string quotation +# │ │ +# ▼ ▼ +# (?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9=_\-]{32})['\"] +# +# ▲ ▲ ▲ +# │ │ │ +# │ │ │ +# identifier assignment symbol +# Secret +# +[[rules]] +id = "gitlab-pat" +description = "GitLab Personal Access Token" +regex = '''glpat-[0-9a-zA-Z\-]{20}''' + +[[rules]] +id = "aws-access-token" +description = "AWS" +regex = '''AKIA[0-9A-Z]{16}''' + +# Cryptographic keys +[[rules]] +id = "PKCS8-PK" +description = "PKCS8 private key" +regex = '''-----BEGIN PRIVATE KEY-----''' + +[[rules]] +id = "RSA-PK" +description = "RSA private key" +regex = '''-----BEGIN RSA PRIVATE KEY-----''' + +[[rules]] +id = "OPENSSH-PK" +description = "SSH private key" +regex = '''-----BEGIN OPENSSH PRIVATE KEY-----''' + +[[rules]] +id = "PGP-PK" +description = "PGP private key" +regex = '''-----BEGIN PGP PRIVATE KEY BLOCK-----''' + +[[rules]] +id = "github-pat" +description = "Github Personal Access Token" +regex = '''ghp_[0-9a-zA-Z]{36}''' + +[[rules]] +id = "github-oauth" +description = "Github OAuth Access Token" +regex = '''gho_[0-9a-zA-Z]{36}''' + +[[rules]] +id = "SSH-DSA-PK" +description = "SSH (DSA) private key" +regex = '''-----BEGIN DSA PRIVATE KEY-----''' + +[[rules]] +id = "SSH-EC-PK" +description = "SSH (EC) private key" +regex = '''-----BEGIN EC PRIVATE KEY-----''' + + +[[rules]] +id = "github-app-token" +description = "Github App Token" +regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}''' + +[[rules]] +id = "github-refresh-token" +description = "Github Refresh Token" +regex = '''ghr_[0-9a-zA-Z]{76}''' + +[[rules]] +id = "shopify-shared-secret" +description = "Shopify shared secret" +regex = '''shpss_[a-fA-F0-9]{32}''' + +[[rules]] +id = "shopify-access-token" +description = "Shopify access token" +regex = '''shpat_[a-fA-F0-9]{32}''' + +[[rules]] +id = "shopify-custom-access-token" +description = "Shopify custom app access token" +regex = '''shpca_[a-fA-F0-9]{32}''' + +[[rules]] +id = "shopify-private-app-access-token" +description = "Shopify private app access token" +regex = '''shppa_[a-fA-F0-9]{32}''' + +[[rules]] +id = "slack-access-token" +description = "Slack token" +regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?''' + +[[rules]] +id = "stripe-access-token" +description = "Stripe" +regex = '''(?i)(sk|pk)_(test|live)_[0-9a-z]{10,32}''' + +[[rules]] +id = "pypi-upload-token" +description = "PyPI upload token" +regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}''' + +[[rules]] +id = "gcp-service-account" +description = "Google (GCP) Service-account" +regex = '''\"type\": \"service_account\"''' + +[[rules]] +id = "heroku-api-key" +description = "Heroku API Key" +regex = ''' (?i)(heroku[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})['\"]''' +secretGroup = 3 + +[[rules]] +id = "slack-web-hook" +description = "Slack Webhook" +regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}''' + +[[rules]] +id = "twilio-api-key" +description = "Twilio API Key" +regex = '''SK[0-9a-fA-F]{32}''' + +[[rules]] +id = "age-secret-key" +description = "Age secret key" +regex = '''AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}''' + +[[rules]] +id = "facebook-token" +description = "Facebook token" +regex = '''(?i)(facebook[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-f0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "twitter-token" +description = "Twitter token" +regex = '''(?i)(twitter[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-f0-9]{35,44})['\"]''' +secretGroup = 3 + +[[rules]] +id = "adobe-client-id" +description = "Adobe Client ID (Oauth Web)" +regex = '''(?i)(adobe[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-f0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "adobe-client-secret" +description = "Adobe Client Secret" +regex = '''(p8e-)(?i)[a-z0-9]{32}''' + +[[rules]] +id = "alibaba-access-key-id" +description = "Alibaba AccessKey ID" +regex = '''(LTAI)(?i)[a-z0-9]{20}''' + +[[rules]] +id = "alibaba-secret-key" +description = "Alibaba Secret Key" +regex = '''(?i)(alibaba[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{30})['\"]''' +secretGroup = 3 + +[[rules]] +id = "asana-client-id" +description = "Asana Client ID" +regex = '''(?i)(asana[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9]{16})['\"]''' +secretGroup = 3 + +[[rules]] +id = "asana-client-secret" +description = "Asana Client Secret" +regex = '''(?i)(asana[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "atlassian-api-token" +description = "Atlassian API token" +regex = '''(?i)(atlassian[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{24})['\"]''' +secretGroup = 3 + +[[rules]] +id = "bitbucket-client-id" +description = "Bitbucket client ID" +regex = '''(?i)(bitbucket[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "bitbucket-client-secret" +description = "Bitbucket client secret" +regex = '''(?i)(bitbucket[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9_\-]{64})['\"]''' +secretGroup = 3 + +[[rules]] +id = "beamer-api-token" +description = "Beamer API token" +regex = '''(?i)(beamer[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"](b_[a-z0-9=_\-]{44})['\"]''' +secretGroup = 3 + +[[rules]] +id = "clojars-api-token" +description = "Clojars API token" +regex = '''(CLOJARS_)(?i)[a-z0-9]{60}''' + +[[rules]] +id = "contentful-delivery-api-token" +description = "Contentful delivery API token" +regex = '''(?i)(contentful[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9\-=_]{43})['\"]''' +secretGroup = 3 + +[[rules]] +id = "contentful-preview-api-token" +description = "Contentful preview API token" +regex = '''(?i)(contentful[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9\-=_]{43})['\"]''' +secretGroup = 3 + +[[rules]] +id = "databricks-api-token" +description = "Databricks API token" +regex = '''dapi[a-h0-9]{32}''' + +[[rules]] +id = "discord-api-token" +description = "Discord API key" +regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]''' +secretGroup = 3 + +[[rules]] +id = "discord-client-id" +description = "Discord client ID" +regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9]{18})['\"]''' +secretGroup = 3 + +[[rules]] +id = "discord-client-secret" +description = "Discord client secret" +regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9=_\-]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "doppler-api-token" +description = "Doppler API token" +regex = '''['\"](dp\.pt\.)(?i)[a-z0-9]{43}['\"]''' + +[[rules]] +id = "dropbox-api-secret" +description = "Dropbox API secret/key" +regex = '''(?i)(dropbox[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{15})['\"]''' + +[[rules]] +id = "dropbox-sl-api-token" +description = "Dropbox short lived API token" +regex = '''(?i)(dropbox[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"](sl\.[a-z0-9\-=_]{135})['\"]''' + +[[rules]] +id = "dropbox-ll-api-token" +description = "Dropbox long lived API token" +regex = '''(?i)(dropbox)(.{0,20})['\"](?i)[a-z0-9]{11}(AAAAAAAAAA)[a-z0-9-_=]{43}['\"]''' + +[[rules]] +id = "duffel-api-token" +description = "Duffel API token" +regex = '''['\"]duffel_(test|live)_(?i)[a-z0-9_-]{43}['\"]''' + +[[rules]] +id = "dynatrace-api-token" +description = "Dynatrace API token" +regex = '''['\"]dt0c01\.(?i)[a-z0-9]{24}\.[a-z0-9]{64}['\"]''' + +[[rules]] +id = "easypost-api-token" +description = "EasyPost API token" +regex = '''['\"]EZAK(?i)[a-z0-9]{54}['\"]''' + +[[rules]] +id = "easypost-test-api-token" +description = "EasyPost test API token" +regex = '''['\"]EZTK(?i)[a-z0-9]{54}['\"]''' + +[[rules]] +id = "fastly-api-token" +description = "Fastly API token" +regex = '''(?i)(fastly[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9\-=_]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "finicity-client-secret" +description = "Finicity client secret" +regex = '''(?i)(finicity[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{20})['\"]''' +secretGroup = 3 + +[[rules]] +id = "finicity-api-token" +description = "Finicity API token" +regex = '''(?i)(finicity[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-f0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "flutterweave-public-key" +description = "Flutterweave public key" +regex = '''FLWPUBK_TEST-(?i)[a-h0-9]{32}-X''' + +[[rules]] +id = "flutterweave-secret-key" +description = "Flutterweave secret key" +regex = '''FLWSECK_TEST-(?i)[a-h0-9]{32}-X''' + +[[rules]] +id = "flutterweave-enc-key" +description = "Flutterweave encrypted key" +regex = '''FLWSECK_TEST[a-h0-9]{12}''' + +[[rules]] +id = "frameio-api-token" +description = "Frame.io API token" +regex = '''fio-u-(?i)[a-z0-9-_=]{64}''' + +[[rules]] +id = "gocardless-api-token" +description = "GoCardless API token" +regex = '''['\"]live_(?i)[a-z0-9-_=]{40}['\"]''' + +[[rules]] +id = "grafana-api-token" +description = "Grafana API token" +regex = '''['\"]eyJrIjoi(?i)[a-z0-9-_=]{72,92}['\"]''' + +[[rules]] +id = "hashicorp-tf-api-token" +description = "Hashicorp Terraform user/org API token" +regex = '''['\"](?i)[a-z0-9]{14}\.atlasv1\.[a-z0-9-_=]{60,70}['\"]''' + +[[rules]] +id = "hubspot-api-token" +description = "Hubspot API token" +regex = '''(?i)(hubspot[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{8}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{12})['\"]''' +secretGroup = 3 + +[[rules]] +id = "intercom-api-token" +description = "Intercom API token" +regex = '''(?i)(intercom[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9=_]{60})['\"]''' +secretGroup = 3 + +[[rules]] +id = "intercom-client-secret" +description = "Intercom client secret/ID" +regex = '''(?i)(intercom[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{8}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{12})['\"]''' +secretGroup = 3 + +[[rules]] +id = "ionic-api-token" +description = "Ionic API token" +regex = '''ion_(?i)[a-z0-9]{42}''' + +[[rules]] +id = "linear-api-token" +description = "Linear API token" +regex = '''lin_api_(?i)[a-z0-9]{40}''' + +[[rules]] +id = "linear-client-secret" +description = "Linear client secret/ID" +regex = '''(?i)(linear[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-f0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "lob-api-key" +description = "Lob API Key" +regex = '''(?i)(lob[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]((live|test)_[a-f0-9]{35})['\"]''' +secretGroup = 3 + +[[rules]] +id = "lob-pub-api-key" +description = "Lob Publishable API Key" +regex = '''(?i)(lob[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]((test|live)_pub_[a-f0-9]{31})['\"]''' +secretGroup = 3 + +[[rules]] +id = "mailchimp-api-key" +description = "Mailchimp API key" +regex = '''(?i)(mailchimp[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-f0-9]{32}-us20)['\"]''' +secretGroup = 3 + +[[rules]] +id = "mailgun-private-api-token" +description = "Mailgun private API token" +regex = '''(?i)(mailgun[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"](key-[a-f0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "mailgun-pub-key" +description = "Mailgun public validation key" +regex = '''(?i)(mailgun[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"](pubkey-[a-f0-9]{32})['\"]''' +secretGroup = 3 + +[[rules]] +id = "mailgun-signing-key" +description = "Mailgun webhook signing key" +regex = '''(?i)(mailgun[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})['\"]''' +secretGroup = 3 + +[[rules]] +id = "mapbox-api-token" +description = "Mapbox API token" +regex = '''(?i)(pk\.[a-z0-9]{60}\.[a-z0-9]{22})''' + +[[rules]] +id = "messagebird-api-token" +description = "MessageBird API token" +regex = '''(?i)(messagebird[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{25})['\"]''' +secretGroup = 3 + +[[rules]] +id = "messagebird-client-id" +description = "MessageBird API client ID" +regex = '''(?i)(messagebird[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{8}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{12})['\"]''' +secretGroup = 3 + +[[rules]] +id = "new-relic-user-api-key" +description = "New Relic user API Key" +regex = '''['\"](NRAK-[A-Z0-9]{27})['\"]''' + +[[rules]] +id = "new-relic-user-api-id" +description = "New Relic user API ID" +regex = '''(?i)(newrelic[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([A-Z0-9]{64})['\"]''' +secretGroup = 3 + +[[rules]] +id = "new-relic-browser-api-token" +description = "New Relic ingest browser API token" +regex = '''['\"](NRJS-[a-f0-9]{19})['\"]''' + +[[rules]] +id = "npm-access-token" +description = "npm access token" +regex = '''['\"](npm_(?i)[a-z0-9]{36})['\"]''' + +[[rules]] +id = "planetscale-password" +description = "Planetscale password" +regex = '''pscale_pw_(?i)[a-z0-9\-_\.]{43}''' + +[[rules]] +id = "planetscale-api-token" +description = "Planetscale API token" +regex = '''pscale_tkn_(?i)[a-z0-9\-_\.]{43}''' + +[[rules]] +id = "postman-api-token" +description = "Postman API token" +regex = '''PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34}''' + +[[rules]] +id = "pulumi-api-token" +description = "Pulumi API token" +regex = '''pul-[a-f0-9]{40}''' + +[[rules]] +id = "rubygems-api-token" +description = "Rubygem API token" +regex = '''rubygems_[a-f0-9]{48}''' + +[[rules]] +id = "sendgrid-api-token" +description = "Sendgrid API token" +regex = '''SG\.(?i)[a-z0-9_\-\.]{66}''' + +[[rules]] +id = "sendinblue-api-token" +description = "Sendinblue API token" +regex = '''xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16}''' + +[[rules]] +id = "shippo-api-token" +description = "Shippo API token" +regex = '''shippo_(live|test)_[a-f0-9]{40}''' + +[[rules]] +id = "linedin-client-secret" +description = "Linkedin Client secret" +regex = '''(?i)(linkedin[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z]{16})['\"]''' +secretGroup = 3 + +[[rules]] +id = "linedin-client-id" +description = "Linkedin Client ID" +regex = '''(?i)(linkedin[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{14})['\"]''' +secretGroup = 3 + +[[rules]] +id = "twitch-api-token" +description = "Twitch API token" +regex = '''(?i)(twitch[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-z0-9]{30})['\"]''' +secretGroup = 3 + +[[rules]] +id = "typeform-api-token" +description = "Typeform API token" +regex = '''(?i)(typeform[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}(tfp_[a-z0-9\-_\.=]{59})''' +secretGroup = 3 + +[[rules]] +id = "generic-api-key" +description = "Generic API Key" +regex = '''(?i)((key|api|token|secret|password)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9a-zA-Z\-_=]{8,64})['\"]''' +entropy = 3.7 +secretGroup = 4 + + +[allowlist] +description = "global allow lists" +regexes = ['''219-09-9999''', '''078-05-1120''', '''(9[0-9]{2}|666)-\d{2}-\d{4}'''] +paths = ['''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$'''] diff --git a/.gitleaksignore b/.gitleaksignore index 3458d63f..402af754 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -17,3 +17,8 @@ daf41a143aa8c483db584ba1e7222e8eafec1d3b:backend/controller.py:generic-api-key:2 af77e930289cbc87987567bff0efc25936484df2:backend/controller.py:generic-api-key:354b04972639d66053109596d3b73a1d91688964ebb:electron/constants/publishOptions.js:github-fine-grained-pat:3 b04972639d66053109596d3b73a1d91688964ebb:electron/constants/publishOptions.js:github-fine-grained-pat:3 af77e930289cbc87987567bff0efc25936484df2:backend/controller.py:generic-api-key:354 +e7de9ce0b902ed6d68f8c5b033d044f39b08f5a1:operate/data/contracts/service_staking_token/contract.yaml:generic-api-key:10 +d8149e9b5b7bd6a7ed7bc1039900702f1d4f287b:operate/services/manage.py:generic-api-key:405 +d8149e9b5b7bd6a7ed7bc1039900702f1d4f287b:operate/services/manage.py:generic-api-key:406 +d8149e9b5b7bd6a7ed7bc1039900702f1d4f287b:operate/services/manage.py:generic-api-key:454 +d8149e9b5b7bd6a7ed7bc1039900702f1d4f287b:operate/services/manage.py:generic-api-key:455 \ No newline at end of file diff --git a/.pylintrc b/.pylintrc index 974242bb..04156b43 100644 --- a/.pylintrc +++ b/.pylintrc @@ -20,7 +20,7 @@ E0611: no-name-in-module R0903: Too few public methods [IMPORTS] -ignored-modules=os,io +ignored-modules=os,io,psutil [DESIGN] # min-public-methods=1 diff --git a/electron/constants/publishOptions.js b/electron/constants/publishOptions.js index c827e041..f94f7670 100644 --- a/electron/constants/publishOptions.js +++ b/electron/constants/publishOptions.js @@ -5,7 +5,7 @@ const publishOptions = { releaseType: 'draft', token: process.env.GH_TOKEN, private: false, - publishAutoUpdate: true, + publishAutoUpdate: false, }; module.exports = { publishOptions }; diff --git a/electron/install.js b/electron/install.js index d30e914f..944ceac5 100644 --- a/electron/install.js +++ b/electron/install.js @@ -1,15 +1,20 @@ // Installation helpers. +const https = require('https'); +const path = require('path'); const fs = require('fs'); const os = require('os'); const sudo = require('sudo-prompt'); const process = require('process'); -const { spawnSync } = require('child_process'); +const axios = require("axios") + const Docker = require('dockerode'); +const { spawnSync } = require('child_process'); -const Version = '0.1.0rc26'; +const Version = '0.1.0rc34'; const OperateDirectory = `${os.homedir()}/.operate`; const VenvDir = `${OperateDirectory}/venv`; +const TempDir = `${OperateDirectory}/temp`; const VersionFile = `${OperateDirectory}/version.txt`; const LogFile = `${OperateDirectory}/logs.txt`; const OperateInstallationLog = `${os.homedir()}/operate.log`; @@ -22,6 +27,20 @@ const SudoOptions = { name: 'Pearl', env: Env, }; +const TendermintUrls = { + darwin: { + x64: "https://github.com/tendermint/tendermint/releases/download/v0.34.19/tendermint_0.34.19_darwin_amd64.tar.gz", + arm64: "https://github.com/tendermint/tendermint/releases/download/v0.34.19/tendermint_0.34.19_darwin_arm64.tar.gz", + }, + linux: { + x64: "https://github.com/tendermint/tendermint/releases/download/v0.34.19/tendermint_0.34.19_linux_amd64.tar.gz", + arm64: "https://github.com/tendermint/tendermint/releases/download/v0.34.19/tendermint_0.34.19_linux_arm64.tar.gz", + }, + win32: { + x64: "https://github.com/tendermint/tendermint/releases/download/v0.34.19/tendermint_0.34.19_windows_amd64.tar.gz", + arm64: "https://github.com/tendermint/tendermint/releases/download/v0.34.19/tendermint_0.34.19_windows_arm64.tar.gz" + } +} function getBinPath(command) { return spawnSync('/usr/bin/which', [command], { env: Env }) @@ -104,6 +123,46 @@ function installBrew() { ]); } +function isTendermintInstalledUnix() { + return Boolean(getBinPath('tendermint')); +} + +async function downloadFile(url, dest) { + const writer = fs.createWriteStream(dest); + try { + const response = await axios({ + url, + method: 'GET', + responseType: 'stream' + }); + response.data.pipe(writer); + return new Promise((resolve, reject) => { + writer.on('finish', resolve); + writer.on('error', reject); + }); + } catch (err) { + fs.unlink(dest, () => { }); // Delete the file if there is an error + console.error('Error downloading the file:', err.message); + } +} + +async function installTendermintUnix() { + const cwd = process.cwd() + process.chdir(TempDir) + + console.log(appendLog(`Installing tendermint for ${os.platform()}-${process.arch}`)) + const url = TendermintUrls[os.platform()][process.arch] + + console.log(appendLog(`Downloading ${url}, might take a while...`)) + await downloadFile(url, `${TempDir}/tendermint.tar.gz`) + + console.log(appendLog(`Installing tendermint binary`)) + await runCmdUnix("tar", ["-xvf", "tendermint.tar.gz"]) + await runSudoUnix("install", "tendermint /usr/local/bin") + process.chdir(cwd) +} + + function isDockerInstalledDarwin() { return Boolean(getBinPath('docker')); } @@ -212,7 +271,7 @@ function versionBumpRequired() { } function removeLogFile() { - if (fs.existsSync()) { + if (fs.existsSync(LogFile)) { fs.rmSync(LogFile); } } @@ -236,13 +295,6 @@ async function setupDarwin(ipcChannel) { installBrew(); } - console.log(appendLog('Checking docker installation')); - if (!isDockerInstalledDarwin()) { - ipcChannel.send('response', 'Installing Pearl Daemon'); - console.log(appendLog('Installing docker')); - installDockerDarwin(); - } - console.log(appendLog('Checking python installation')); if (!isPythonInstalledDarwin()) { ipcChannel.send('response', 'Installing Pearl Daemon'); @@ -254,6 +306,13 @@ async function setupDarwin(ipcChannel) { await createDirectory(`${OperateDirectory}`); await createDirectory(`${OperateDirectory}/temp`); + console.log(appendLog('Checking tendermint installation')); + if (!isTendermintInstalledUnix()) { + ipcChannel.send('response', 'Installing Pearl Daemon'); + console.log(appendLog('Installing tendermint')); + await installTendermintUnix() + } + if (!fs.existsSync(VenvDir)) { ipcChannel.send('response', 'Installing Pearl Daemon'); console.log(appendLog('Creating virtual environment')); @@ -279,14 +338,9 @@ async function setupDarwin(ipcChannel) { await installOperateCli('/opt/homebrew/bin/operate'); } +// TODO: Add Tendermint installation async function setupUbuntu(ipcChannel) { removeInstallationLogFile(); - console.log(appendLog('Checking docker installation')); - if (!isDockerInstalledUbuntu()) { - ipcChannel.send('response', 'Installing Pearl Daemon'); - console.log(appendLog('Installing docker')); - await installDockerUbuntu(); - } console.log(appendLog('Checking python installation')); if (!isPythonInstalledUbuntu()) { @@ -306,9 +360,11 @@ async function setupUbuntu(ipcChannel) { await createDirectory(`${OperateDirectory}`); await createDirectory(`${OperateDirectory}/temp`); - if (versionBumpRequired()) { - // removePreviousInstallation(); - writeVersion(); + console.log(appendLog('Checking tendermint installation')); + if (!isTendermintInstalledUnix()) { + ipcChannel.send('response', 'Installing Pearl Daemon'); + console.log(appendLog('Installing tendermint')); + await installTendermintUnix() } if (!fs.existsSync(VenvDir)) { diff --git a/electron/main.js b/electron/main.js index c30d7b3e..c820346d 100644 --- a/electron/main.js +++ b/electron/main.js @@ -24,7 +24,6 @@ const { setupUbuntu, OperateCmd, OperateDirectory, - startDocker, Env, dirs, } = require('./install'); @@ -442,8 +441,6 @@ ipcMain.on('check', async function (event, _argument) { } } - startDocker(event.sender); - if (isDev) { event.sender.send( 'response', diff --git a/operate/cli.py b/operate/cli.py index 6d8c9c66..821fa88a 100644 --- a/operate/cli.py +++ b/operate/cli.py @@ -29,13 +29,6 @@ from pathlib import Path from aea.helpers.logging import setup_logger -from autonomy.constants import ( - AUTONOMY_IMAGE_NAME, - AUTONOMY_IMAGE_VERSION, - TENDERMINT_IMAGE_NAME, - TENDERMINT_IMAGE_VERSION, -) -from autonomy.deploy.generators.docker_compose.base import get_docker_client from clea import group, params, run from compose.project import ProjectError from docker.errors import APIError @@ -159,23 +152,6 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st shutdown_endpoint ) - def pull_latest_images() -> None: - """Pull latest docker images.""" - logger.info("Pulling latest images") - client = get_docker_client() - - logger.info(f"Pulling {AUTONOMY_IMAGE_NAME}:{AUTONOMY_IMAGE_VERSION}") - client.images.pull( - repository=AUTONOMY_IMAGE_NAME, - tag=AUTONOMY_IMAGE_VERSION, - ) - - logger.info(f"Pulling {TENDERMINT_IMAGE_NAME}:{TENDERMINT_IMAGE_VERSION}") - client.images.pull( - repository=TENDERMINT_IMAGE_NAME, - tag=TENDERMINT_IMAGE_VERSION, - ) - def schedule_funding_job( service: str, from_safe: bool = True, @@ -203,9 +179,7 @@ def cancel_funding_job(service: str) -> None: if not status: logger.info(f"Funding job cancellation for {service} failed") - app = FastAPI( - on_startup=[pull_latest_images], - ) + app = FastAPI() app.add_middleware( CORSMiddleware, diff --git a/operate/services/manage.py b/operate/services/manage.py index 230704da..803ee852 100644 --- a/operate/services/manage.py +++ b/operate/services/manage.py @@ -402,8 +402,8 @@ def deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,too if service.chain_data.on_chain_state == OnChainState.MINTED: cost_of_bond = user_params.cost_of_bond if user_params.use_staking: - token_utility = "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" - olas_token = "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" + token_utility = "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" # nosec + olas_token = "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" # nosec self.logger.info( f"Approving OLAS as bonding token from {wallet.safe} to {token_utility}" ) @@ -451,8 +451,8 @@ def deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,too if service.chain_data.on_chain_state == OnChainState.ACTIVATED: cost_of_bond = user_params.cost_of_bond if user_params.use_staking: - token_utility = "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" - olas_token = "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" + token_utility = "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" # nosec + olas_token = "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" # nosec self.logger.info( f"Approving OLAS as bonding token from {wallet.safe} to {token_utility}" ) @@ -865,7 +865,7 @@ def deploy_service_locally(self, hash: str, force: bool = True) -> Deployment: deployment.start() return deployment - def stop_service_locally(self, hash: str, delete: bool) -> Deployment: + def stop_service_locally(self, hash: str, delete: bool = False) -> Deployment: """ Stop service locally diff --git a/operate/services/service.py b/operate/services/service.py index 6853e388..ce3d75ea 100644 --- a/operate/services/service.py +++ b/operate/services/service.py @@ -21,23 +21,38 @@ import json import os +import platform import shutil +import signal +import subprocess # nosec +import time import typing as t from copy import copy, deepcopy from dataclasses import dataclass from pathlib import Path -from typing import Any, List, Optional, Tuple - -from aea.configurations.constants import SKILL +from venv import main as venv_cli + +import psutil +from aea.__version__ import __version__ as aea_version +from aea.configurations.constants import ( + DEFAULT_LEDGER, + LEDGER, + PRIVATE_KEY, + PRIVATE_KEY_PATH_SCHEMA, + SKILL, +) from aea.configurations.data_types import PackageType from aea.helpers.yaml_utils import yaml_dump, yaml_load, yaml_load_all from aea_cli_ipfs.ipfs_utils import IPFSTool +from autonomy.__version__ import __version__ as autonomy_version from autonomy.cli.helpers.deployment import run_deployment, stop_deployment from autonomy.configurations.loader import load_service_config +from autonomy.deploy.base import BaseDeploymentGenerator from autonomy.deploy.base import ServiceBuilder as BaseServiceBuilder from autonomy.deploy.constants import ( AGENT_KEYS_DIR, BENCHMARKS_DIR, + DEFAULT_ENCODING, LOG_DIR, PERSISTENT_DATA_DIR, TM_STATE_DIR, @@ -55,6 +70,7 @@ from operate.http.exceptions import NotAllowed from operate.keys import Keys from operate.resource import LocalResource +from operate.services.utils import tendermint from operate.types import ( ChainType, DeployedNodes, @@ -72,6 +88,7 @@ ALL_PARTICIPANTS = "all_participants" CONSENSUS_THRESHOLD = "consensus_threshold" + # pylint: disable=no-member,redefined-builtin,too-many-instance-attributes DUMMY_MULTISIG = "0xm" @@ -158,14 +175,14 @@ def try_update_ledger_params(self, chain: str, address: str) -> None: def try_update_runtime_params( self, - multisig_address: Optional[str] = None, - agent_instances: Optional[List[str]] = None, - consensus_threshold: Optional[int] = None, - service_id: Optional[int] = None, + multisig_address: t.Optional[str] = None, + agent_instances: t.Optional[t.List[str]] = None, + consensus_threshold: t.Optional[int] = None, + service_id: t.Optional[int] = None, ) -> None: """Try and update setup parameters.""" - param_overrides: List[Tuple[str, Any]] = [] + param_overrides: t.List[t.Tuple[str, t.Any]] = [] if multisig_address is not None: param_overrides.append( (SAFE_CONTRACT_ADDRESS, multisig_address), @@ -243,6 +260,310 @@ def deployment_config(self) -> DeploymentConfig: return DeploymentConfig(self.config.json.get("deployment", {})) # type: ignore +# TODO: Port back to open-autonomy +class HostDeploymentGenerator(BaseDeploymentGenerator): + """Host deployment.""" + + output_name: str = "runtime.json" + deployment_type: str = "host" + + def generate_config_tendermint(self) -> "HostDeploymentGenerator": + """Generate tendermint configuration.""" + tmhome = str(self.build_dir / "node") + subprocess.run( # pylint: disable=subprocess-run-check # nosec + args=[ + str( + shutil.which("tendermint"), + ), + "--home", + tmhome, + "init", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + # TODO: Dynamic port allocation + params = { + "TMHOME": tmhome, + "TMSTATE": str(self.build_dir / "tm_state"), + "P2P_LADDR": "tcp://localhost:26656", + "RPC_LADDR": "tcp://localhost:26657", + "PROXY_APP": "tcp://localhost:26658", + "CREATE_EMPTY_BLOCKS": "true", + "USE_GRPC": "false", + "FLASK_APP": "tendermint:create_server", + } + (self.build_dir / "tendermint.json").write_text( + json.dumps(params, indent=2), + encoding="utf-8", + ) + shutil.copy( + tendermint.__file__, + self.build_dir / "tendermint.py", + ) + return self + + def generate( + self, + image_version: t.Optional[str] = None, + use_hardhat: bool = False, + use_acn: bool = False, + ) -> "HostDeploymentGenerator": + """Generate agent and tendermint configurations""" + agent = self.service_builder.generate_agent(agent_n=0) + agent = {key: f"{value}" for key, value in agent.items()} + (self.build_dir / "agent.json").write_text( + json.dumps(agent, indent=2), + encoding="utf-8", + ) + venv_cli(args=[str(self.build_dir / "venv")]) + return self + + def _populate_keys(self) -> None: + """Populate the keys directory""" + # TODO: Add multiagent support + kp, *_ = t.cast(t.List[t.Dict[str, str]], self.service_builder.keys) + key = kp[PRIVATE_KEY] + ledger = kp.get(LEDGER, DEFAULT_LEDGER) + keys_file = self.build_dir / PRIVATE_KEY_PATH_SCHEMA.format(ledger) + keys_file.write_text(key, encoding=DEFAULT_ENCODING) + + def _populate_keys_multiledger(self) -> None: + """Populate the keys directory with multiple set of keys""" + + def populate_private_keys(self) -> "DockerComposeGenerator": + """Populate the private keys to the build directory for host mapping.""" + if self.service_builder.multiledger: + self._populate_keys_multiledger() + else: + self._populate_keys() + return self + + +def _run_cmd(args: t.List[str], cwd: t.Optional[Path] = None) -> None: + """Run command in a subprocess.""" + print(f"Running: {' '.join(args)}") + result = subprocess.run( # pylint: disable=subprocess-run-check # nosec + args=args, + cwd=cwd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + if result.returncode != 0: + raise RuntimeError(f"Error running: {args} @ {cwd}\n{result.stderr.decode()}") + + +def _setup_agent(working_dir: Path) -> None: + """Setup agent.""" + env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8")) + # Patch for trader agent + if "SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_POLICY_STORE_PATH" in env: + data_dir = working_dir / "data" + data_dir.mkdir(exist_ok=True) + env["SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_POLICY_STORE_PATH"] = str(data_dir) + + # TODO: Dynamic port allocation, backport to service builder + env["CONNECTION_ABCI_CONFIG_HOST"] = "localhost" + env["CONNECTION_ABCI_CONFIG_PORT"] = "26658" + + for var in env: + # Fix tendermint connection params + if var.endswith("MODELS_PARAMS_ARGS_TENDERMINT_COM_URL"): + env[var] = "http://localhost:8080" + + if var.endswith("MODELS_PARAMS_ARGS_TENDERMINT_URL"): + env[var] = "http://localhost:26657" + + if var.endswith("MODELS_PARAMS_ARGS_TENDERMINT_P2P_URL"): + env[var] = "localhost:26656" + + if var.endswith("MODELS_BENCHMARK_TOOL_ARGS_LOG_DIR"): + benchmarks_dir = working_dir / "benchmarks" + benchmarks_dir.mkdir(exist_ok=True, parents=True) + env[var] = str(benchmarks_dir.resolve()) + + (working_dir / "agent.json").write_text( + json.dumps(env, indent=4), + encoding="utf-8", + ) + venv = working_dir / "venv" + pbin = str(venv / "bin" / "python") + + # Install agent dependencies + _run_cmd( + args=[ + pbin, + "-m", + "pip", + "install", + f"open-autonomy[all]=={autonomy_version}", + f"open-aea-ledger-ethereum=={aea_version}", + f"open-aea-ledger-ethereum-flashbots=={aea_version}", + f"open-aea-ledger-cosmos=={aea_version}", + ], + ) + + # Install tendermint dependencies + _run_cmd(args=[pbin, "-m", "pip", "install", "flask", "requests"]) + + abin = str(venv / "bin" / "aea") + # Fetch agent + _run_cmd( + args=[ + abin, + "init", + "--reset", + "--author", + "valory", + "--remote", + "--ipfs", + "--ipfs-node", + "/dns/registry.autonolas.tech/tcp/443/https", + ], + cwd=working_dir, + ) + _run_cmd( + args=[ + abin, + "fetch", + env["AEA_AGENT"], + "--alias", + "agent", + ], + cwd=working_dir, + ) + + # Install agent dependencies + _run_cmd( + args=[abin, "-v", "debug", "install", "--timeout", "600"], + cwd=working_dir / "agent", + ) + + # Add keys + shutil.copy( + working_dir / "ethereum_private_key.txt", + working_dir / "agent" / "ethereum_private_key.txt", + ) + _run_cmd( + args=[abin, "add-key", "ethereum"], + cwd=working_dir / "agent", + ) + _run_cmd( + args=[abin, "issue-certificates"], + cwd=working_dir / "agent", + ) + + +def _start_agent(working_dir: Path) -> None: + """Start agent process.""" + env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8")) + process = subprocess.Popen( # pylint: disable=consider-using-with # nosec + args=[str(working_dir / "venv" / "bin" / "aea"), "run"], + cwd=working_dir / "agent", + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + env={**os.environ, **env}, + creationflags=( + 0x00000008 if platform.system() == "Windows" else 0 + ), # Detach process from the main process + ) + (working_dir / "agent.pid").write_text( + data=str(process.pid), + encoding="utf-8", + ) + + +def _start_tendermint(working_dir: Path) -> None: + """Start tendermint process.""" + env = json.loads((working_dir / "tendermint.json").read_text(encoding="utf-8")) + process = subprocess.Popen( # pylint: disable=consider-using-with # nosec + args=[ + str(working_dir / "venv" / "bin" / "flask"), + "run", + "--host", + "localhost", + "--port", + "8080", + ], + cwd=working_dir, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + env={**os.environ, **env}, + creationflags=( + 0x00000008 if platform.system() == "Windows" else 0 + ), # Detach process from the main process + ) + (working_dir / "tendermint.pid").write_text( + data=str(process.pid), + encoding="utf-8", + ) + + +def _kill_process(pid: int) -> None: + """Kill process.""" + print(f"Trying to kill process: {pid}") + while True: + if not psutil.pid_exists(pid=pid): + return + if psutil.Process(pid=pid).status() in ( + psutil.STATUS_DEAD, + psutil.STATUS_ZOMBIE, + ): + return + try: + os.kill( + pid, + ( + signal.CTRL_C_EVENT # type: ignore + if platform.platform() == "Windows" + else signal.SIGKILL + ), + ) + except OSError: + return + time.sleep(1) + + +def _stop_agent(working_dir: Path) -> None: + """Start process.""" + pid = working_dir / "agent.pid" + if not pid.exists(): + return + _kill_process(int(pid.read_text(encoding="utf-8"))) + + +def _stop_tendermint(working_dir: Path) -> None: + """Start tendermint process.""" + pid = working_dir / "tendermint.pid" + if not pid.exists(): + return + _kill_process(int(pid.read_text(encoding="utf-8"))) + + +def run_host_deployment(build_dir: Path) -> None: + """Run host deployment.""" + _setup_agent( + working_dir=build_dir, + ) + _start_tendermint( + working_dir=build_dir, + ) + _start_agent( + working_dir=build_dir, + ) + + +def stop_host_deployment(build_dir: Path) -> None: + """Stop host deployment.""" + _stop_agent( + working_dir=build_dir, + ) + _stop_tendermint( + working_dir=build_dir, + ) + + @dataclass class Deployment(LocalResource): """Deployment resource for a service.""" @@ -273,13 +594,11 @@ def load(cls, path: Path) -> "Deployment": """Load a service""" return super().load(path) # type: ignore - def build(self, force: bool = True) -> None: - """ - Build a deployment - - :param force: Remove existing deployment and build a new one - :return: Deployment object - """ + def _build_docker( + self, + force: bool = True, + ) -> None: + """Build docker deployment.""" service = Service.load(path=self.path) # Remove network from cache if exists, this will raise an error # if the service is still running so we can do an early exit @@ -321,17 +640,15 @@ def build(self, force: bool = True) -> None: builder.try_update_runtime_params( multisig_address=service.chain_data.multisig, agent_instances=service.chain_data.instances, - consensus_threshold=None, service_id=service.chain_data.token, + consensus_threshold=None, ) # TODO: Support for multiledger builder.try_update_ledger_params( chain=LedgerType(service.ledger_config.type).name.lower(), address=service.ledger_config.rpc, ) - volumes = builder.service.deployment_config.get("agent", {}).pop( - "volumes", {} - ) + # build deployment ( DockerComposeGenerator( @@ -361,14 +678,15 @@ def build(self, force: bool = True) -> None: ) _volumes = [] - for volume, mount in volumes.items(): + for volume, mount in ( + service.helper.deployment_config().get("volumes", {}).items() + ): (build / volume).mkdir(exist_ok=True) _volumes.append(f"./{volume}:{mount}:Z") for node in deployment["services"]: if "abci" in node: deployment["services"][node]["volumes"].extend(_volumes) - # Mech request patch if ( "SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_MECH_REQUEST_PRICE=0" in deployment["services"][node]["environment"] @@ -386,7 +704,102 @@ def build(self, force: bool = True) -> None: self.status = DeploymentStatus.BUILT self.store() - def start(self) -> None: + def _build_host(self, force: bool = True) -> None: + """Build host depployment.""" + build = self.path / DEPLOYMENT + if build.exists() and not force: + return + + if build.exists() and force: + stop_host_deployment(build_dir=build) + shutil.rmtree(build) + + service = Service.load(path=self.path) + if service.helper.config.number_of_agents > 1: + raise RuntimeError( + "Host deployment currently only supports single agent deployments" + ) + + keys_file = self.path / KEYS_JSON + keys_file.write_text( + json.dumps( + [ + { + "address": key.address, + "private_key": key.private_key, + "ledger": key.ledger.name.lower(), + } + for key in service.keys + ], + indent=4, + ), + encoding="utf-8", + ) + try: + builder = ServiceBuilder.from_dir( + path=service.service_path, + keys_file=keys_file, + number_of_agents=len(service.keys), + ) + builder.deplopyment_type = HostDeploymentGenerator.deployment_type + builder.try_update_abci_connection_params() + builder.try_update_runtime_params( + multisig_address=service.chain_data.multisig, + agent_instances=service.chain_data.instances, + service_id=service.chain_data.token, + consensus_threshold=None, + ) + # TODO: Support for multiledger + builder.try_update_ledger_params( + chain=LedgerType(service.ledger_config.type).name.lower(), + address=service.ledger_config.rpc, + ) + + ( + HostDeploymentGenerator( + service_builder=builder, + build_dir=build.resolve(), + use_tm_testnet_setup=True, + ) + .generate_config_tendermint() + .generate() + .populate_private_keys() + ) + + except Exception as e: + shutil.rmtree(build) + raise e + + # Mech price patch. + agent_vars = json.loads(Path(build, "agent.json").read_text(encoding="utf-8")) + if "SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_MECH_REQUEST_PRICE" in agent_vars: + agent_vars[ + "SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_MECH_REQUEST_PRICE" + ] = "10000000000000000" + Path(build, "agent.json").write_text( + json.dumps(agent_vars, indent=4), + encoding="utf-8", + ) + + self.status = DeploymentStatus.BUILT + self.store() + + def build( + self, + use_docker: bool = False, + force: bool = True, + ) -> None: + """ + Build a deployment + + :param force: Remove existing deployment and build a new one + :return: Deployment object + """ + if use_docker: + return self._build_docker(force=force) + return self._build_host(force=force) + + def start(self, use_docker: bool = False) -> None: """Start the service""" if self.status != DeploymentStatus.BUILT: raise NotAllowed( @@ -396,13 +809,20 @@ def start(self) -> None: self.status = DeploymentStatus.DEPLOYING self.store() - build = self.path / "deployment" - run_deployment(build_dir=build, detach=True) + try: + if use_docker: + run_deployment(build_dir=self.path / "deployment", detach=True) + else: + run_host_deployment(build_dir=self.path / "deployment") + except Exception: + self.status = DeploymentStatus.BUILT + self.store() + raise self.status = DeploymentStatus.DEPLOYED self.store() - def stop(self) -> None: + def stop(self, use_docker: bool = False) -> None: """Stop the deployment.""" if self.status != DeploymentStatus.DEPLOYED: return @@ -410,8 +830,10 @@ def stop(self) -> None: self.status = DeploymentStatus.STOPPING self.store() - # Stop the docker deployment - stop_deployment(build_dir=self.path / "deployment") + if use_docker: + stop_deployment(build_dir=self.path / "deployment") + else: + stop_host_deployment(build_dir=self.path / "deployment") self.status = DeploymentStatus.BUILT self.store() diff --git a/operate/services/utils/__init__.py b/operate/services/utils/__init__.py new file mode 100644 index 00000000..ebf20738 --- /dev/null +++ b/operate/services/utils/__init__.py @@ -0,0 +1 @@ +"""Deployment utils.""" diff --git a/operate/services/utils/tendermint.py b/operate/services/utils/tendermint.py new file mode 100644 index 00000000..9d69d1e2 --- /dev/null +++ b/operate/services/utils/tendermint.py @@ -0,0 +1,639 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2021-2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""Tendermint manager.""" +import json +import logging +import os +import platform +import re +import shutil +import signal +import stat +import subprocess # nosec: +import sys +import traceback +from logging import Logger +from pathlib import Path +from threading import Event, Thread +from typing import Any, Callable, Dict, List, Optional, Tuple, cast + +import requests +from flask import Flask, Response, jsonify, request +from werkzeug.exceptions import InternalServerError, NotFound + + +ENCODING = "utf-8" +DEFAULT_LOG_FILE = "com.log" +DEFAULT_TENDERMINT_LOG_FILE = "tendermint.log" + +CONFIG_OVERRIDE = [ + ("fast_sync = true", "fast_sync = false"), + ("max_num_outbound_peers = 10", "max_num_outbound_peers = 0"), + ("pex = true", "pex = false"), +] +TM_STATUS_ENDPOINT = "http://localhost:26657/status" + +_TCP = "tcp://" +ENCODING = "utf-8" +DEFAULT_P2P_LISTEN_ADDRESS = f"{_TCP}0.0.0.0:26656" +DEFAULT_RPC_LISTEN_ADDRESS = f"{_TCP}0.0.0.0:26657" + +IS_DEV_MODE = False + +logging.basicConfig( + filename=os.environ.get("LOG_FILE", DEFAULT_LOG_FILE), + level=logging.DEBUG, + format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s", # noqa : W1309 +) + + +class StoppableThread( + Thread, +): + """Thread class with a stop() method.""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + """Initialise the thread.""" + super().__init__(*args, **kwargs) + self._stop_event = Event() + + def stop(self) -> None: + """Set the stop event.""" + self._stop_event.set() + + def stopped(self) -> bool: + """Check if the thread is stopped.""" + return self._stop_event.is_set() + + +class TendermintParams: # pylint: disable=too-few-public-methods + """Tendermint node parameters.""" + + def __init__( # pylint: disable=too-many-arguments + self, + proxy_app: str, + rpc_laddr: str = DEFAULT_RPC_LISTEN_ADDRESS, + p2p_laddr: str = DEFAULT_P2P_LISTEN_ADDRESS, + p2p_seeds: Optional[List[str]] = None, + consensus_create_empty_blocks: bool = True, + home: Optional[str] = None, + use_grpc: bool = False, + ): + """ + Initialize the parameters to the Tendermint node. + + :param proxy_app: ABCI address. + :param rpc_laddr: RPC address. + :param p2p_laddr: P2P address. + :param p2p_seeds: P2P seeds. + :param consensus_create_empty_blocks: if true, Tendermint node creates empty blocks. + :param home: Tendermint's home directory. + :param use_grpc: Whether to use a gRPC server, or TCP + """ + + self.proxy_app = proxy_app + self.rpc_laddr = rpc_laddr + self.p2p_laddr = p2p_laddr + self.p2p_seeds = p2p_seeds + self.consensus_create_empty_blocks = consensus_create_empty_blocks + self.home = home + self.use_grpc = use_grpc + + def __str__(self) -> str: + """Get the string representation.""" + return ( + f"{self.__class__.__name__}(" + f" proxy_app={self.proxy_app},\n" + f" rpc_laddr={self.rpc_laddr},\n" + f" p2p_laddr={self.p2p_laddr},\n" + f" p2p_seeds={self.p2p_seeds},\n" + f" consensus_create_empty_blocks={self.consensus_create_empty_blocks},\n" + f" home={self.home},\n" + ")" + ) + + def build_node_command(self, debug: bool = False) -> List[str]: + """Build the 'node' command.""" + p2p_seeds = ",".join(self.p2p_seeds) if self.p2p_seeds else "" + cmd = [ + "tendermint", + "node", + f"--proxy_app={self.proxy_app}", + f"--rpc.laddr={self.rpc_laddr}", + f"--p2p.laddr={self.p2p_laddr}", + f"--p2p.seeds={p2p_seeds}", + f"--consensus.create_empty_blocks={str(self.consensus_create_empty_blocks).lower()}", + f"--abci={'grpc' if self.use_grpc else 'socket'}", + ] + if debug: + cmd.append("--log_level=debug") + if self.home is not None: # pragma: nocover + cmd += ["--home", self.home] + return cmd + + @staticmethod + def get_node_command_kwargs() -> Dict: + """Get the node command kwargs""" + kwargs = { + "bufsize": 1, + "universal_newlines": True, + "stdout": subprocess.PIPE, + "stderr": subprocess.STDOUT, + } + if platform.system() == "Windows": # pragma: nocover + kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore + else: + kwargs["preexec_fn"] = os.setsid # type: ignore + return kwargs + + +class TendermintNode: + """A class to manage a Tendermint node.""" + + def __init__( + self, + params: TendermintParams, + logger: Optional[Logger] = None, + write_to_log: bool = False, + ): + """ + Initialize a Tendermint node. + + :param params: the parameters. + :param logger: the logger. + :param write_to_log: Write to log file. + """ + self.params = params + self._process: Optional[subprocess.Popen] = None + self._monitoring: Optional[StoppableThread] = None + self._stopping = False + self.logger = logger or logging.getLogger() + self.log_file = os.environ.get("LOG_FILE", DEFAULT_TENDERMINT_LOG_FILE) + self.write_to_log = write_to_log + + def _build_init_command(self) -> List[str]: + """Build the 'init' command.""" + cmd = [ + "tendermint", + "init", + ] + if self.params.home is not None: # pragma: nocover + cmd += ["--home", self.params.home] + return cmd + + def init(self) -> None: + """Initialize Tendermint node.""" + cmd = self._build_init_command() + subprocess.call(cmd) # nosec + + def _monitor_tendermint_process( + self, + ) -> None: + """Check server status.""" + if self._monitoring is None: + raise ValueError("Monitoring is not running") + self.log("Monitoring thread started\n") + while not self._monitoring.stopped(): + try: + if self._process is not None and self._process.stdout is not None: + line = self._process.stdout.readline() + self.log(line) + for trigger in [ + # this occurs when we lose connection from the tm side + "RPC HTTP server stopped", + # whenever the node is stopped because of a closed connection + # from on any of the tendermint modules (abci, p2p, rpc, etc) + # we restart the node + "Stopping abci.socketClient for error: read message: EOF", + ]: + if self._monitoring.stopped(): + break + if line.find(trigger) >= 0: + self._stop_tm_process() + # we can only reach this step if monitoring was activated + # so we make sure that after reset the monitoring continues + self._start_tm_process() + self.log( + f"Restarted the HTTP RPC server, as a connection was dropped with message:\n\t\t {line}\n" + ) + except Exception as e: # pylint: disable=broad-except + self.log(f"Error!: {str(e)}") + self.log("Monitoring thread terminated\n") + + def _start_tm_process(self, debug: bool = False) -> None: + """Start a Tendermint node process.""" + if self._process is not None or self._stopping: # pragma: nocover + return + cmd = self.params.build_node_command(debug) + kwargs = self.params.get_node_command_kwargs() + self.log(f"Starting Tendermint: {cmd}\n") + self._process = ( + subprocess.Popen( # nosec # pylint: disable=consider-using-with,W1509 + cmd, **kwargs + ) + ) + self.log("Tendermint process started\n") + + def _start_monitoring_thread(self) -> None: + """Start a monitoring thread.""" + self._monitoring = StoppableThread(target=self._monitor_tendermint_process) + self._monitoring.start() + + def start(self, debug: bool = False) -> None: + """Start a Tendermint node process.""" + self._start_tm_process(debug) + self._start_monitoring_thread() + + def _stop_tm_process(self) -> None: + """Stop a Tendermint node process.""" + if self._process is None or self._stopping: + return + + self._stopping = True + if platform.system() == "Windows": + self._win_stop_tm() + else: + # this will raise an exception if the process + # is not terminated within the specified timeout + self._unix_stop_tm() + + self._stopping = False + self._process = None + self.log("Tendermint process stopped\n") + + def _win_stop_tm(self) -> None: + """Stop a Tendermint node process on Windows.""" + os.kill(self._process.pid, signal.CTRL_C_EVENT) # type: ignore # pylint: disable=no-member + try: + self._process.wait(timeout=5) # type: ignore + except subprocess.TimeoutExpired: # nosec + os.kill(self._process.pid, signal.CTRL_BREAK_EVENT) # type: ignore # pylint: disable=no-member + + def _unix_stop_tm(self) -> None: + """Stop a Tendermint node process on Unix.""" + self._process.send_signal(signal.SIGTERM) # type: ignore + try: + self._process.wait(timeout=5) # type: ignore + except subprocess.TimeoutExpired: # nosec + self.log("Tendermint process did not stop gracefully\n") + + # if the process is still running poll will return None + poll = self._process.poll() # type: ignore + if poll is not None: + return + + self._process.terminate() # type: ignore + self._process.wait(3) # type: ignore + + def _stop_monitoring_thread(self) -> None: + """Stop a monitoring process.""" + if self._monitoring is not None: + self._monitoring.stop() # set stop event + self._monitoring.join() + + def stop(self) -> None: + """Stop a Tendermint node process.""" + self._stop_tm_process() + self._stop_monitoring_thread() + + @staticmethod + def _write_to_console(line: str) -> None: + """Write line to console.""" + sys.stdout.write(str(line)) + sys.stdout.flush() + + def _write_to_file(self, line: str) -> None: + """Write line to console.""" + with open(self.log_file, "a", encoding=ENCODING) as file: + file.write(line) + + def log(self, line: str) -> None: + """Open and write a line to the log file.""" + self._write_to_console(line=line) + if self.write_to_log: + self._write_to_file(line=line) + + def prune_blocks(self) -> int: + """Prune blocks from the Tendermint state""" + return subprocess.call( # nosec: + [ + "tendermint", + "--home", + str(self.params.home), + "unsafe-reset-all", + ] + ) + + def reset_genesis_file( + self, + genesis_time: str, + initial_height: str, + period_count: str, + ) -> None: + """Reset genesis file.""" + + genesis_file = Path(str(self.params.home), "config", "genesis.json") + genesis_config = json.loads(genesis_file.read_text(encoding=ENCODING)) + genesis_config["genesis_time"] = genesis_time + genesis_config["initial_height"] = initial_height + genesis_config["chain_id"] = f"autonolas-{period_count}" + genesis_file.write_text(json.dumps(genesis_config, indent=2), encoding=ENCODING) + + +def load_genesis() -> Any: + """Load genesis file.""" + return json.loads( + Path(os.environ["TMHOME"], "config", "genesis.json").read_text( + encoding=ENCODING + ) + ) + + +def get_defaults() -> Dict[str, str]: + """Get defaults from genesis file.""" + genesis = load_genesis() + return dict(genesis_time=genesis.get("genesis_time")) + + +def override_config_toml() -> None: + """Update sync method.""" + + config_path = str(Path(os.environ["TMHOME"]) / "config" / "config.toml") + logging.info(config_path) + with open(config_path, "r", encoding=ENCODING) as fp: + config = fp.read() + + for old, new in CONFIG_OVERRIDE: + config = config.replace(old, new) + + with open(config_path, "w+", encoding=ENCODING) as fp: + fp.write(config) + + +def update_peers(validators: List[Dict], config_path: Path) -> None: + """Fix peers.""" + + config_text = config_path.read_text(encoding="utf-8") + + new_peer_string = 'persistent_peers = "' + for peer in validators: + hostname = peer["hostname"] + if hostname in ("localhost", "0.0.0.0"): # nosec + # This (tendermint) node will be running in a docker container and no other node + # will be running in the same container. If we receive either localhost or 0.0.0.0, + # we make an assumption that the address belongs to a node running on the + # same machine with a different docker container and different p2p port so, + # we replace the hostname with the docker's internal host url. + hostname = "localhost" + new_peer_string += ( + peer["peer_id"] + "@" + hostname + ":" + str(peer["p2p_port"]) + "," + ) + new_peer_string = new_peer_string[:-1] + '"\n' + + updated_config = re.sub('persistent_peers = ".*\n', new_peer_string, config_text) + config_path.write_text(updated_config, encoding="utf-8") + + +def update_external_address(external_address: str, config_path: Path) -> None: + """Update the external address.""" + config_text = config_path.read_text(encoding="utf-8") + new_external_address = f'external_address = "{external_address}"\n' + updated_config = re.sub( + 'external_address = ".*\n', new_external_address, config_text + ) + config_path.write_text(updated_config, encoding="utf-8") + + +def update_genesis_config(data: Dict) -> None: + """Update genesis.json file for the tendermint node.""" + + genesis_file = Path(os.environ["TMHOME"]) / "config" / "genesis.json" + genesis_data = {} + genesis_data["genesis_time"] = data["genesis_config"]["genesis_time"] + genesis_data["chain_id"] = data["genesis_config"]["chain_id"] + genesis_data["initial_height"] = "0" + genesis_data["consensus_params"] = data["genesis_config"]["consensus_params"] + genesis_data["validators"] = [ + { + "address": validator["address"], + "pub_key": validator["pub_key"], + "power": validator["power"], + "name": validator["name"], + } + for validator in data["validators"] + ] + genesis_data["app_hash"] = "" + genesis_file.write_text(json.dumps(genesis_data, indent=2), encoding=ENCODING) + + +class PeriodDumper: + """Dumper for tendermint data.""" + + resets: int + dump_dir: Path + logger: logging.Logger + + def __init__(self, logger: logging.Logger, dump_dir: Optional[Path] = None) -> None: + """Initialize object.""" + + self.resets = 0 + self.logger = logger + self.dump_dir = Path(dump_dir or "/tm_state") + + if self.dump_dir.is_dir(): + shutil.rmtree(str(self.dump_dir), onerror=self.readonly_handler) + self.dump_dir.mkdir(exist_ok=True) + + @staticmethod + def readonly_handler( + func: Callable, path: str, execinfo: Any # pylint: disable=unused-argument + ) -> None: + """If permission is readonly, we change and retry.""" + try: + os.chmod(path, stat.S_IWRITE) + func(path) + except (FileNotFoundError, OSError): + return + + def dump_period(self) -> None: + """Dump tendermint run data for replay""" + store_dir = self.dump_dir / f"period_{self.resets}" + store_dir.mkdir(exist_ok=True) + try: + shutil.copytree( + os.environ["TMHOME"], str(store_dir / ("node" + os.environ["ID"])) + ) + self.logger.info(f"Dumped data for period {self.resets}") + except OSError as e: + self.logger.info( + f"Error occurred while dumping data for period {self.resets}: {e}" + ) + self.resets += 1 + + +def create_app( # pylint: disable=too-many-statements + debug: bool = False, +) -> Tuple[Flask, TendermintNode]: + """Create the Tendermint server app""" + write_to_log = os.environ.get("WRITE_TO_LOG", "false").lower() == "true" + tendermint_params = TendermintParams( + proxy_app=os.environ["PROXY_APP"], + p2p_laddr=os.environ["P2P_LADDR"], + rpc_laddr=os.environ["RPC_LADDR"], + consensus_create_empty_blocks=os.environ["CREATE_EMPTY_BLOCKS"] == "true", + home=os.environ["TMHOME"], + use_grpc=os.environ["USE_GRPC"] == "true", + ) + + app = Flask(__name__) + period_dumper = PeriodDumper( + logger=app.logger, + dump_dir=Path(os.environ["TMSTATE"]), + ) + tendermint_node = TendermintNode( + tendermint_params, + logger=app.logger, + write_to_log=write_to_log, + ) + tendermint_node.init() + override_config_toml() + tendermint_node.start(debug=debug) + + @app.get("/params") + def get_params() -> Dict: + """Get tendermint params.""" + try: + priv_key_file = ( + Path(os.environ["TMHOME"]) / "config" / "priv_validator_key.json" + ) + priv_key_data = json.loads(priv_key_file.read_text(encoding=ENCODING)) + del priv_key_data["priv_key"] + status = requests.get(TM_STATUS_ENDPOINT).json() + priv_key_data["peer_id"] = status["result"]["node_info"]["id"] + return { + "params": priv_key_data, + "status": True, + "error": None, + } + except (FileNotFoundError, json.JSONDecodeError): + return {"params": {}, "status": False, "error": traceback.format_exc()} + + @app.post("/params") + def update_params() -> Dict: + """Update validator params.""" + + try: + data: Dict = json.loads(request.get_data().decode(ENCODING)) + cast(logging.Logger, app.logger).debug( # pylint: disable=no-member + f"Data update requested with data={data}" + ) + + cast(logging.Logger, app.logger).info( # pylint: disable=no-member + "Updating genesis config." + ) + update_genesis_config(data=data) + + cast(logging.Logger, app.logger).info( # pylint: disable=no-member + "Updating peristent peers." + ) + config_path = Path(os.environ["TMHOME"]) / "config" / "config.toml" + update_peers( + validators=data["validators"], + config_path=config_path, + ) + update_external_address( + external_address=data["external_address"], + config_path=config_path, + ) + + return {"status": True, "error": None} + except (FileNotFoundError, json.JSONDecodeError, PermissionError): + return {"status": False, "error": traceback.format_exc()} + + @app.route("/gentle_reset") + def gentle_reset() -> Tuple[Any, int]: + """Reset the tendermint node gently.""" + try: + tendermint_node.stop() + tendermint_node.start() + return jsonify({"message": "Reset successful.", "status": True}), 200 + except Exception as e: # pylint: disable=W0703 + return jsonify({"message": f"Reset failed: {e}", "status": False}), 200 + + @app.route("/app_hash") + def app_hash() -> Tuple[Any, int]: + """Get the app hash.""" + try: + non_routable, loopback = "0.0.0.0", "127.0.0.1" # nosec + endpoint = f"{tendermint_params.rpc_laddr.replace('tcp', 'http').replace(non_routable, loopback)}/block" + height = request.args.get("height") + params = {"height": height} if height is not None else None + res = requests.get(endpoint, params) + app_hash_ = res.json()["result"]["block"]["header"]["app_hash"] + return jsonify({"app_hash": app_hash_}), res.status_code + except Exception as e: # pylint: disable=W0703 + return ( + jsonify({"error": f"Could not get the app hash: {str(e)}"}), + 200, + ) + + @app.route("/hard_reset") + def hard_reset() -> Tuple[Any, int]: + """Reset the node forcefully, and prune the blocks""" + try: + tendermint_node.stop() + if IS_DEV_MODE: + period_dumper.dump_period() + + return_code = tendermint_node.prune_blocks() + if return_code: + tendermint_node.start() + raise RuntimeError("Could not perform `unsafe-reset-all` successfully!") + defaults = get_defaults() + tendermint_node.reset_genesis_file( + request.args.get("genesis_time", defaults["genesis_time"]), + # default should be 1: https://github.com/tendermint/tendermint/pull/5191/files + request.args.get("initial_height", "1"), + request.args.get("period_count", "0"), + ) + tendermint_node.start() + return jsonify({"message": "Reset successful.", "status": True}), 200 + except Exception as e: # pylint: disable=W0703 + return jsonify({"message": f"Reset failed: {e}", "status": False}), 200 + + @app.errorhandler(404) # type: ignore + def handle_notfound(e: NotFound) -> Response: + """Handle server error.""" + cast(logging.Logger, app.logger).info(e) # pylint: disable=E + return Response("Not Found", status=404, mimetype="application/json") + + @app.errorhandler(500) # type: ignore + def handle_server_error(e: InternalServerError) -> Response: + """Handle server error.""" + cast(logging.Logger, app.logger).info(e) # pylint: disable=E + return Response("Error Closing Node", status=500, mimetype="application/json") + + return app, tendermint_node + + +def create_server() -> Any: + """Function to retrieve just the app to be used by flask entry point.""" + flask_app, _ = create_app() + return flask_app diff --git a/package.json b/package.json index 6b4c8cfe..62d35fa3 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "@ant-design/icons": "^5.3.0", "@fontsource/inter": "^5.0.17", "antd": "^5.14.0", + "axios": "^1.7.2", "child_process": "^1.0.2", "cross-env": "^7.0.3", "dockerode": "^4.0.2", @@ -53,5 +54,5 @@ "start": "electron .", "build": "rm -rf dist/ && electron-builder build" }, - "version": "0.1.0-rc26.1" + "version": "0.1.0-rc34" } diff --git a/poetry.lock b/poetry.lock index 6b453044..198bc7af 100644 --- a/poetry.lock +++ b/poetry.lock @@ -502,63 +502,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.2" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:554c7327bf0fd688050348e22db7c8e163fb7219f3ecdd4732d7ed606b417263"}, - {file = "coverage-7.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d0305e02e40c7cfea5d08d6368576537a74c0eea62b77633179748d3519d6705"}, - {file = "coverage-7.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:829fb55ad437d757c70d5b1c51cfda9377f31506a0a3f3ac282bc6a387d6a5f1"}, - {file = "coverage-7.5.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:894b1acded706f1407a662d08e026bfd0ff1e59e9bd32062fea9d862564cfb65"}, - {file = "coverage-7.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe76d6dee5e4febefa83998b17926df3a04e5089e3d2b1688c74a9157798d7a2"}, - {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c7ebf2a37e4f5fea3c1a11e1f47cea7d75d0f2d8ef69635ddbd5c927083211fc"}, - {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20e611fc36e1a0fc7bbf957ef9c635c8807d71fbe5643e51b2769b3cc0fb0b51"}, - {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c5c5b7ae2763533152880d5b5b451acbc1089ade2336b710a24b2b0f5239d20"}, - {file = "coverage-7.5.2-cp310-cp310-win32.whl", hash = "sha256:1e4225990a87df898e40ca31c9e830c15c2c53b1d33df592bc8ef314d71f0281"}, - {file = "coverage-7.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:976cd92d9420e6e2aa6ce6a9d61f2b490e07cb468968adf371546b33b829284b"}, - {file = "coverage-7.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5997d418c219dcd4dcba64e50671cca849aaf0dac3d7a2eeeb7d651a5bd735b8"}, - {file = "coverage-7.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec27e93bbf5976f0465e8936f02eb5add99bbe4e4e7b233607e4d7622912d68d"}, - {file = "coverage-7.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f11f98753800eb1ec872562a398081f6695f91cd01ce39819e36621003ec52a"}, - {file = "coverage-7.5.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e34680049eecb30b6498784c9637c1c74277dcb1db75649a152f8004fbd6646"}, - {file = "coverage-7.5.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e12536446ad4527ac8ed91d8a607813085683bcce27af69e3b31cd72b3c5960"}, - {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3d3f7744b8a8079d69af69d512e5abed4fb473057625588ce126088e50d05493"}, - {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:431a3917e32223fcdb90b79fe60185864a9109631ebc05f6c5aa03781a00b513"}, - {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a7c6574225f34ce45466f04751d957b5c5e6b69fca9351db017c9249786172ce"}, - {file = "coverage-7.5.2-cp311-cp311-win32.whl", hash = "sha256:2b144d142ec9987276aeff1326edbc0df8ba4afbd7232f0ca10ad57a115e95b6"}, - {file = "coverage-7.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:900532713115ac58bc3491b9d2b52704a05ed408ba0918d57fd72c94bc47fba1"}, - {file = "coverage-7.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9a42970ce74c88bdf144df11c52c5cf4ad610d860de87c0883385a1c9d9fa4ab"}, - {file = "coverage-7.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26716a1118c6ce2188283b4b60a898c3be29b480acbd0a91446ced4fe4e780d8"}, - {file = "coverage-7.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60b66b0363c5a2a79fba3d1cd7430c25bbd92c923d031cae906bdcb6e054d9a2"}, - {file = "coverage-7.5.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d22eba19273b2069e4efeff88c897a26bdc64633cbe0357a198f92dca94268"}, - {file = "coverage-7.5.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bb5b92a0ab3d22dfdbfe845e2fef92717b067bdf41a5b68c7e3e857c0cff1a4"}, - {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1aef719b6559b521ae913ddeb38f5048c6d1a3d366865e8b320270b7bc4693c2"}, - {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8809c0ea0e8454f756e3bd5c36d04dddf222989216788a25bfd6724bfcee342c"}, - {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1acc2e2ef098a1d4bf535758085f508097316d738101a97c3f996bccba963ea5"}, - {file = "coverage-7.5.2-cp312-cp312-win32.whl", hash = "sha256:97de509043d3f0f2b2cd171bdccf408f175c7f7a99d36d566b1ae4dd84107985"}, - {file = "coverage-7.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:8941e35a0e991a7a20a1fa3e3182f82abe357211f2c335a9e6007067c3392fcf"}, - {file = "coverage-7.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5662bf0f6fb6757f5c2d6279c541a5af55a39772c2362ed0920b27e3ce0e21f7"}, - {file = "coverage-7.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d9c62cff2ffb4c2a95328488fd7aa96a7a4b34873150650fe76b19c08c9c792"}, - {file = "coverage-7.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74eeaa13e8200ad72fca9c5f37395fb310915cec6f1682b21375e84fd9770e84"}, - {file = "coverage-7.5.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f29bf497d51a5077994b265e976d78b09d9d0dff6ca5763dbb4804534a5d380"}, - {file = "coverage-7.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f96aa94739593ae0707eda9813ce363a0a0374a810ae0eced383340fc4a1f73"}, - {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:51b6cee539168a912b4b3b040e4042b9e2c9a7ad9c8546c09e4eaeff3eacba6b"}, - {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:59a75e6aa5c25b50b5a1499f9718f2edff54257f545718c4fb100f48d570ead4"}, - {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29da75ce20cb0a26d60e22658dd3230713c6c05a3465dd8ad040ffc991aea318"}, - {file = "coverage-7.5.2-cp38-cp38-win32.whl", hash = "sha256:23f2f16958b16152b43a39a5ecf4705757ddd284b3b17a77da3a62aef9c057ef"}, - {file = "coverage-7.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:9e41c94035e5cdb362beed681b58a707e8dc29ea446ea1713d92afeded9d1ddd"}, - {file = "coverage-7.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06d96b9b19bbe7f049c2be3c4f9e06737ec6d8ef8933c7c3a4c557ef07936e46"}, - {file = "coverage-7.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:878243e1206828908a6b4a9ca7b1aa8bee9eb129bf7186fc381d2646f4524ce9"}, - {file = "coverage-7.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:482df956b055d3009d10fce81af6ffab28215d7ed6ad4a15e5c8e67cb7c5251c"}, - {file = "coverage-7.5.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a35c97af60a5492e9e89f8b7153fe24eadfd61cb3a2fb600df1a25b5dab34b7e"}, - {file = "coverage-7.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb4c7859a3f757a116521d4d3a8a82befad56ea1bdacd17d6aafd113b0071e"}, - {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e1046aab24c48c694f0793f669ac49ea68acde6a0798ac5388abe0a5615b5ec8"}, - {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:448ec61ea9ea7916d5579939362509145caaecf03161f6f13e366aebb692a631"}, - {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4a00bd5ba8f1a4114720bef283cf31583d6cb1c510ce890a6da6c4268f0070b7"}, - {file = "coverage-7.5.2-cp39-cp39-win32.whl", hash = "sha256:9f805481d5eff2a96bac4da1570ef662bf970f9a16580dc2c169c8c3183fa02b"}, - {file = "coverage-7.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:2c79f058e7bec26b5295d53b8c39ecb623448c74ccc8378631f5cb5c16a7e02c"}, - {file = "coverage-7.5.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:40dbb8e7727560fe8ab65efcddfec1ae25f30ef02e2f2e5d78cfb52a66781ec5"}, - {file = "coverage-7.5.2.tar.gz", hash = "sha256:13017a63b0e499c59b5ba94a8542fb62864ba3016127d1e4ef30d354fc2b00e9"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -1610,13 +1610,13 @@ files = [ [[package]] name = "netaddr" -version = "1.2.1" +version = "1.3.0" description = "A network address manipulation library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "netaddr-1.2.1-py3-none-any.whl", hash = "sha256:bd9e9534b0d46af328cf64f0e5a23a5a43fca292df221c85580b27394793496e"}, - {file = "netaddr-1.2.1.tar.gz", hash = "sha256:6eb8fedf0412c6d294d06885c110de945cf4d22d2b510d0404f4e06950857987"}, + {file = "netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe"}, + {file = "netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a"}, ] [package.extras] @@ -1813,6 +1813,34 @@ files = [ {file = "protobuf-4.24.4.tar.gz", hash = "sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667"}, ] +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "py-multibase" version = "1.0.3" @@ -1899,18 +1927,18 @@ files = [ [[package]] name = "pydantic" -version = "2.7.1" +version = "2.7.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, - {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, + {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"}, + {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.2" +pydantic-core = "2.18.3" typing-extensions = ">=4.6.1" [package.extras] @@ -1918,90 +1946,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.2" +version = "2.18.3" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, - {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, - {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, - {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, - {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, - {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, - {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, - {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, - {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, - {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, - {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, - {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, - {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, - {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"}, + {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"}, + {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"}, + {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"}, + {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"}, + {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"}, + {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"}, + {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"}, + {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"}, + {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"}, + {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"}, + {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"}, ] [package.dependencies] @@ -2827,4 +2855,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more [metadata] lock-version = "2.0" python-versions = "<4.0,>=3.8" -content-hash = "1e992d521bc2e69dfaf469567dd51899bb73a80df3ba7e1ad36d50947b4e0319" +content-hash = "5fa061baee21213f2d471e79e7c00fd6f4bca217f7d53bd74c29e0637d447bd3" diff --git a/pyproject.toml b/pyproject.toml index 45d42e3a..fe6eee3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "olas-operate-middleware" -version = "0.1.0-rc26" +version = "0.1.0-rc34" description = "" authors = ["David Vilela ", "Viraj Patel "] readme = "README.md" @@ -43,6 +43,7 @@ requests-toolbelt = "1.0.0" starlette = "==0.36.3" uvicorn = "==0.27.0" web3 = "==6.1.0" +psutil = "^5.9.8" [build-system] requires = ["poetry-core"] diff --git a/tox.ini b/tox.ini index 5123d41c..ccc7c289 100644 --- a/tox.ini +++ b/tox.ini @@ -75,7 +75,7 @@ skip_install = True deps = tomte[safety]==0.2.15 commands = - safety check -i 37524 -i 38038 -i 37776 -i 38039 -i 39621 -i 40291 -i 39706 -i 41002 -i 51358 -i 51499 + safety check -i 37524 -i 38038 -i 37776 -i 38039 -i 39621 -i 40291 -i 39706 -i 41002 -i 51358 -i 51499 -i 67599 [testenv:vulture] skipsdist = True @@ -206,3 +206,12 @@ ignore_missing_imports = True [mypy-compose.*] ignore_missing_imports = True + +[mypy-flask.*] +ignore_missing_imports = True + +[mypy-werkzeug.*] +ignore_missing_imports = True + +[mypy-psutil.*] +ignore_missing_imports = True diff --git a/yarn.lock b/yarn.lock index aa757376..442af96c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1548,6 +1548,15 @@ atomically@^2.0.2: stubborn-fs "^1.2.5" when-exit "^2.1.1" +axios@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" + integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -2858,7 +2867,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -follow-redirects@^1.12.1: +follow-redirects@^1.12.1, follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== @@ -4170,6 +4179,11 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + ps-tree@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"