From a2dfdbedb59259875faa2a5c8053ed35bbdad781 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 10:01:56 +0300 Subject: [PATCH 01/19] refactor: switch to poetry, add interactive deployment --- .coveragerc | 2 + .drone.yml | 60 + .gitignore | 7 + .pylintrc | 402 +++ README.md | 61 +- cic/__init__.py | 3 +- cic/cmd/export.py | 143 +- cic/cmd/ext.py | 2 +- cic/cmd/wizard.py | 390 +++ cic/contract/__init__.py | 0 cic/{ => contract}/base.py | 0 cic/contract/components/__init__.py | 0 cic/{ => contract/components}/attachment.py | 60 +- cic/contract/components/meta.py | 163 ++ cic/{ => contract/components}/network.py | 19 +- cic/contract/components/proof.py | 192 ++ cic/contract/components/token.py | 123 + cic/contract/contract.py | 191 ++ cic/contract/helpers.py | 120 + cic/{ => contract}/processor.py | 47 +- cic/data/config/config.ini | 23 +- cic/data/token_template_v0.json | 7 +- cic/ext/eth/__init__.py | 277 ++- cic/ext/eth/rpc.py | 11 +- cic/ext/eth/start.py | 8 +- cic/extension.py | 141 +- cic/hash.py | 10 - cic/keystore.py | 4 +- cic/meta.py | 141 -- cic/proof.py | 181 -- cic/runnable/cic_cmd.py | 28 +- cic/token.py | 88 - cic/utils.py | 24 + cic/{output.py => writers.py} | 14 +- config/dev-docker/config.ini | 29 + config/prod/config.ini | 29 + eth_requirements.txt | 7 - poetry.lock | 2429 +++++++++++++++++++ pyproject.toml | 94 + requirements.txt | 5 - run_tests.sh | 17 - setup.cfg | 32 - setup.py | 35 - test_requirements.txt | 7 - tests/__init__.py | 0 tests/base_cic.py | 69 +- tests/eth/base_eth.py | 12 +- tests/eth/test_eth_full.py | 6 +- tests/eth/test_eth_offline.py | 2 +- tests/eth/test_eth_sign.py | 4 +- tests/test_keyfile.py | 31 +- tests/test_meta.py | 42 +- tests/test_processor.py | 62 +- tests/test_proof.py | 30 +- tests/{test_output.py => test_writers.py} | 2 +- tests/testdata/proof/meta.json | 7 +- 56 files changed, 4921 insertions(+), 972 deletions(-) create mode 100644 .coveragerc create mode 100644 .drone.yml create mode 100644 .pylintrc create mode 100644 cic/cmd/wizard.py create mode 100644 cic/contract/__init__.py rename cic/{ => contract}/base.py (100%) create mode 100644 cic/contract/components/__init__.py rename cic/{ => contract/components}/attachment.py (52%) create mode 100644 cic/contract/components/meta.py rename cic/{ => contract/components}/network.py (88%) create mode 100644 cic/contract/components/proof.py create mode 100644 cic/contract/components/token.py create mode 100644 cic/contract/contract.py create mode 100644 cic/contract/helpers.py rename cic/{ => contract}/processor.py (71%) delete mode 100644 cic/hash.py delete mode 100644 cic/meta.py delete mode 100644 cic/proof.py delete mode 100644 cic/token.py create mode 100644 cic/utils.py rename cic/{output.py => writers.py} (87%) create mode 100644 config/dev-docker/config.ini create mode 100644 config/prod/config.ini delete mode 100644 eth_requirements.txt create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 run_tests.sh delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test_requirements.txt create mode 100644 tests/__init__.py rename tests/{test_output.py => test_writers.py} (94%) diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..3f351be --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[report] +omit = diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..4d5bb3a --- /dev/null +++ b/.drone.yml @@ -0,0 +1,60 @@ +--- +################ +# Test # +################ + +kind: pipeline +name: default +type: docker + +steps: + # Run tests against Python with pytest + - name: test + image: python:3.8 + commands: + # Install dependencies + - pip install poetry + - poetry install + - poetry run pylint cic + - poetry run pytest + environment: + LOGLEVEL: info + + volumes: + - name: poetry_cache + path: /root/.cache/pypoetry + - name: pip_cache + path: /root/.cache/pip + - name: publish + image: python:3.8 + commands: + # Install dependencies + - pip install poetry + - poetry install + - poetry run semantic-release publish + depends_on: + - test + when: + branch: + - master + environment: + LOGLEVEL: info + GIT_SSL_NO_VERIFY: 1 + REPOSITORY_USERNAME: __token__ + REPOSITORY_PASSWORD: + from_secret: pypi_token + GITEA_TOKEN: + from_secret: gitea_token + + volumes: + - name: poetry_cache + path: /root/.cache/pypoetry + - name: pip_cache + path: /root/.cache/pip +volumes: + - name: poetry_cache + host: + path: /tmp/cache/drone/pypoetry + - name: pip_cache + host: + path: /tmp/cache/drone/pip diff --git a/.gitignore b/.gitignore index e581009..451613e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ __pycache__ *.pyc *.egg-info +.venv +build +.vscode +.idea +contracts +*.egg +.coverage \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..f9f8918 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,402 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=third_party + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns=object_detection_grpc_client.py,prediction_pb2.py,prediction_pb2_grpc.py + +# Pickle collected data for later comparisons. +persistent=no + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# +# Kubeflow disables string-interpolation because we are starting to use f +# style strings +disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,missing-docstring,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating,relative-import,invalid-name,bad-continuation,no-member,locally-disabled,fixme,import-error,too-many-locals,no-name-in-module,too-many-instance-attributes,no-self-use,logging-fstring-interpolation + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". This option is deprecated +# and it will be removed in Pylint 2.0. +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=140 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +# Use 2 spaces consistent with TensorFlow style. +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=7 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=0 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception \ No newline at end of file diff --git a/README.md b/README.md index 1736aac..e8889c2 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,65 @@ -# CIC token deployment tool +# CIC Token Deployment Tool CIC-CLI provides tooling to generate and publish metadata in relation to token deployments. -To install the project (replacing \ with the current version: -0.0.1): - -``` shell -python setup.py sdist -pip install --extra-index-url https://pip.grassrootseconomics.net:8433 dist/cic-.tar.gz +```shell +pip install --extra-index-url https://pip.grassrootseconomics.net cic ``` -## Structure of the components +## Usage +### Using the wizard +``` +# Local +cic wizard ./somewhere -c ./config/dev-docker -![image](data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8IS0tIEdlbmVyYXRlZCBieSBncmFwaHZpeiB2ZXJzaW9uIDIuNDkuMCAoMCkKIC0tPgo8IS0tIFBhZ2VzOiAxIC0tPgo8c3ZnIHdpZHRoPSI0NTZwdCIgaGVpZ2h0PSIyNjBwdCIKIHZpZXdCb3g9IjAuMDAgMC4wMCA0NTUuNTAgMjYwLjAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGcgaWQ9ImdyYXBoMCIgY2xhc3M9ImdyYXBoIiB0cmFuc2Zvcm09InNjYWxlKDEgMSkgcm90YXRlKDApIHRyYW5zbGF0ZSg0IDI1NikiPgo8cG9seWdvbiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgcG9pbnRzPSItNCw0IC00LC0yNTYgNDUxLjUsLTI1NiA0NTEuNSw0IC00LDQiLz4KPCEtLSBpbW11dGFibGVfZGF0YSAtLT4KPGcgaWQ9Im5vZGUxIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5pbW11dGFibGVfZGF0YTwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iNDQ3LjUsLTM2IDI4MS41LC0zNiAyODEuNSwwIDQ0Ny41LDAgNDQ3LjUsLTM2Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjM2NC41IiB5PSItMTQuMyIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5JbW11dGFibGUgbWV0YWRhdGE8L3RleHQ+CjwvZz4KPCEtLSBtdXRhYmxlX2RhdGEgLS0+CjxnIGlkPSJub2RlMiIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bXV0YWJsZV9kYXRhPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSIyNjMuNSwtMzYgMTE1LjUsLTM2IDExNS41LDAgMjYzLjUsMCAyNjMuNSwtMzYiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTg5LjUiIHk9Ii0xNC4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPk11dGFibGUgbWV0YWRhdGE8L3RleHQ+CjwvZz4KPCEtLSB0b2tlbl9zbWFydF9jb250cmFjdCAtLT4KPGcgaWQ9Im5vZGUzIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT50b2tlbl9zbWFydF9jb250cmFjdDwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iOTcsLTM2IDAsLTM2IDAsMCA5NywwIDk3LC0zNiIvPgo8dGV4dCB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSI0OC41IiB5PSItMTQuMyIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5Ub2tlbiBjb2RlPC90ZXh0Pgo8L2c+CjwhLS0gbmV0d29yayAtLT4KPGcgaWQ9Im5vZGU0IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uZXR3b3JrPC90aXRsZT4KPGVsbGlwc2UgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgY3g9IjEyOC41IiBjeT0iLTIzNCIgcng9IjQ4Ljk5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTI4LjUiIHk9Ii0yMzAuMyIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5uZXR3b3JrPC90ZXh0Pgo8L2c+CjwhLS0gZXh0ZW5zaW9uIC0tPgo8ZyBpZD0ibm9kZTUiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmV4dGVuc2lvbjwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSIxODkuNSIgY3k9Ii0xNjIiIHJ4PSI1NS43OSIgcnk9IjE4Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE4OS41IiB5PSItMTU4LjMiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxNC4wMCI+ZXh0ZW5zaW9uPC90ZXh0Pgo8L2c+CjwhLS0gbmV0d29yayYjNDU7Jmd0O2V4dGVuc2lvbiAtLT4KPGcgaWQ9ImVkZ2UxIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uZXR3b3JrJiM0NTsmZ3Q7ZXh0ZW5zaW9uPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTE0Mi42NSwtMjE2Ljc2QzE1MC4yNCwtMjA4LjA2IDE1OS43NCwtMTk3LjE1IDE2OC4yMSwtMTg3LjQzIi8+Cjxwb2x5Z29uIGZpbGw9ImJsYWNrIiBzdHJva2U9ImJsYWNrIiBwb2ludHM9IjE3MS4wNywtMTg5LjQ3IDE3NS4wMSwtMTc5LjYzIDE2NS44LC0xODQuODcgMTcxLjA3LC0xODkuNDciLz4KPC9nPgo8IS0tIHRva2VuIC0tPgo8ZyBpZD0ibm9kZTYiIGNsYXNzPSJub2RlIj4KPHRpdGxlPnRva2VuPC90aXRsZT4KPGVsbGlwc2UgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgY3g9Ijc0LjUiIGN5PSItOTAiIHJ4PSIzNi4yOSIgcnk9IjE4Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9Ijc0LjUiIHk9Ii04Ni4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPnRva2VuPC90ZXh0Pgo8L2c+CjwhLS0gZXh0ZW5zaW9uJiM0NTsmZ3Q7dG9rZW4gLS0+CjxnIGlkPSJlZGdlMiIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZXh0ZW5zaW9uJiM0NTsmZ3Q7dG9rZW48L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMTY0LjU1LC0xNDUuODFDMTQ3LjE4LC0xMzUuMjQgMTIzLjg5LC0xMjEuMDcgMTA1LjM2LC0xMDkuNzgiLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMTA3LjA1LC0xMDYuNzEgOTYuNjgsLTEwNC41IDEwMy40MSwtMTEyLjY5IDEwNy4wNSwtMTA2LjcxIi8+CjwvZz4KPCEtLSBwcm9vZiAtLT4KPGcgaWQ9Im5vZGU4IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5wcm9vZjwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSIzNDUuNSIgY3k9Ii05MCIgcng9IjM1LjE5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMzQ1LjUiIHk9Ii04Ni4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPnByb29mPC90ZXh0Pgo8L2c+CjwhLS0gZXh0ZW5zaW9uJiM0NTsmZ3Q7cHJvb2YgLS0+CjxnIGlkPSJlZGdlNSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZXh0ZW5zaW9uJiM0NTsmZ3Q7cHJvb2Y8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMjIwLjY5LC0xNDdDMjQ2LjY3LC0xMzUuMzUgMjgzLjYzLC0xMTguNzYgMzEwLjU5LC0xMDYuNjciLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMzEyLjM0LC0xMDkuNzIgMzIwLjAzLC0xMDIuNDMgMzA5LjQ3LC0xMDMuMzMgMzEyLjM0LC0xMDkuNzIiLz4KPC9nPgo8IS0tIG1ldGEgLS0+CjxnIGlkPSJub2RlOSIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bWV0YTwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSIxODkuNSIgY3k9Ii05MCIgcng9IjM0LjM5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTg5LjUiIHk9Ii04Ni4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPm1ldGE8L3RleHQ+CjwvZz4KPCEtLSBleHRlbnNpb24mIzQ1OyZndDttZXRhIC0tPgo8ZyBpZD0iZWRnZTYiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmV4dGVuc2lvbiYjNDU7Jmd0O21ldGE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMTg5LjUsLTE0My43QzE4OS41LC0xMzUuOTggMTg5LjUsLTEyNi43MSAxODkuNSwtMTE4LjExIi8+Cjxwb2x5Z29uIGZpbGw9ImJsYWNrIiBzdHJva2U9ImJsYWNrIiBwb2ludHM9IjE5MywtMTE4LjEgMTg5LjUsLTEwOC4xIDE4NiwtMTE4LjEgMTkzLC0xMTguMSIvPgo8L2c+CjwhLS0gdG9rZW4mIzQ1OyZndDt0b2tlbl9zbWFydF9jb250cmFjdCAtLT4KPGcgaWQ9ImVkZ2UzIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT50b2tlbiYjNDU7Jmd0O3Rva2VuX3NtYXJ0X2NvbnRyYWN0PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTY4LjIxLC03Mi4wNUM2NS4yOCwtNjQuMTggNjEuNzMsLTU0LjYyIDU4LjQ1LC00NS43OSIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSI2MS42OCwtNDQuNDMgNTQuOTIsLTM2LjI4IDU1LjEyLC00Ni44NyA2MS42OCwtNDQuNDMiLz4KPC9nPgo8IS0tIHByb2Nlc3NvciAtLT4KPGcgaWQ9Im5vZGU3IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5wcm9jZXNzb3I8L3RpdGxlPgo8ZWxsaXBzZSBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBjeD0iMjUxLjUiIGN5PSItMjM0IiByeD0iNTUuNzkiIHJ5PSIxOCIvPgo8dGV4dCB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNTEuNSIgeT0iLTIzMC4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPnByb2Nlc3NvcjwvdGV4dD4KPC9nPgo8IS0tIHByb2Nlc3NvciYjNDU7Jmd0O2V4dGVuc2lvbiAtLT4KPGcgaWQ9ImVkZ2U0IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5wcm9jZXNzb3ImIzQ1OyZndDtleHRlbnNpb248L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMjM2LjgxLC0yMTYuNDFDMjI5LjA5LC0yMDcuNjkgMjE5LjQ4LC0xOTYuODUgMjEwLjk0LC0xODcuMjEiLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMjEzLjM0LC0xODQuNjQgMjA0LjA5LC0xNzkuNDcgMjA4LjEsLTE4OS4yOCAyMTMuMzQsLTE4NC42NCIvPgo8L2c+CjwhLS0gcHJvb2YmIzQ1OyZndDtpbW11dGFibGVfZGF0YSAtLT4KPGcgaWQ9ImVkZ2U5IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5wcm9vZiYjNDU7Jmd0O2ltbXV0YWJsZV9kYXRhPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTM1MC4xLC03Mi4wNUMzNTIuMjEsLTY0LjI2IDM1NC43OCwtNTQuODIgMzU3LjE1LC00Ni4wOCIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSIzNjAuNTcsLTQ2Ljg0IDM1OS44MSwtMzYuMjggMzUzLjgxLC00NS4wMSAzNjAuNTcsLTQ2Ljg0Ii8+CjwvZz4KPCEtLSBtZXRhJiM0NTsmZ3Q7bXV0YWJsZV9kYXRhIC0tPgo8ZyBpZD0iZWRnZTciIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm1ldGEmIzQ1OyZndDttdXRhYmxlX2RhdGE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBkPSJNMTg5LjUsLTcxLjdDMTg5LjUsLTYzLjk4IDE4OS41LC01NC43MSAxODkuNSwtNDYuMTEiLz4KPHBvbHlnb24gZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHBvaW50cz0iMTkzLC00Ni4xIDE4OS41LC0zNi4xIDE4NiwtNDYuMSAxOTMsLTQ2LjEiLz4KPC9nPgo8IS0tIGF0dGFjaG1lbnQgLS0+CjxnIGlkPSJub2RlMTAiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmF0dGFjaG1lbnQ8L3RpdGxlPgo8ZWxsaXBzZSBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBjeD0iMzQ1LjUiIGN5PSItMTYyIiByeD0iNjMuODkiIHJ5PSIxOCIvPgo8dGV4dCB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNDUuNSIgeT0iLTE1OC4zIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTQuMDAiPmF0dGFjaG1lbnQ8L3RleHQ+CjwvZz4KPCEtLSBhdHRhY2htZW50JiM0NTsmZ3Q7cHJvb2YgLS0+CjxnIGlkPSJlZGdlOCIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+YXR0YWNobWVudCYjNDU7Jmd0O3Byb29mPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgZD0iTTM0NS41LC0xNDMuN0MzNDUuNSwtMTM1Ljk4IDM0NS41LC0xMjYuNzEgMzQ1LjUsLTExOC4xMSIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSIzNDksLTExOC4xIDM0NS41LC0xMDguMSAzNDIsLTExOC4xIDM0OSwtMTE4LjEiLz4KPC9nPgo8L2c+Cjwvc3ZnPgo=) - -CIC-CLI is designed to interface any network type backend. The current -state of the package contains interface to EVM only. Thus, the examples -below are limited to the context of the EVM. - -## Preparing for EVM token deployment +# Production +cic wizard ./somewhere -c ./config/prod +``` +### Modular Some of the concepts described below assume familiarity with base concepts of the CIC architecture. Please refer to the appropriate documentation for more information. To initialize a new token deployment for the EVM: -``` shell +```shell cic init --target eth --name --symbol --precision ``` To automatically fill in settings detected in the network for the EVM: -``` shell +```shell cic ext --registry -d -i -p eth ``` + + +## Structure of the components + +![image](./doc/sphinx/components.svg) + +CIC-CLI is designed to interface any network type backend. The current +state of the package contains interface to EVM only. Thus, the examples +below are limited to the context of the EVM. + +## Development +### Requirements + - [poetry](https://python-poetry.org/docs/#installation) +### Setup + +``` + poetry install +``` + +### Running the CLI + +```bash + poetry run cic -h +``` + +### Tests + +``` +poetry run pytest +``` diff --git a/cic/__init__.py b/cic/__init__.py index 9c13049..3b93d0b 100644 --- a/cic/__init__.py +++ b/cic/__init__.py @@ -1,2 +1 @@ -from .proof import Proof -from .processor import Processor +__version__ = "0.0.2" diff --git a/cic/cmd/export.py b/cic/cmd/export.py index a873b41..33a714c 100644 --- a/cic/cmd/export.py +++ b/cic/cmd/export.py @@ -1,39 +1,49 @@ # standard imports -import logging import importlib +import logging import os +from typing import Optional +# local imports +from cic import ContractProcessor, Proof +from cic.attachment import Attachment +from cic.meta import Meta, MetadataWriter +from cic.network import Network +from cic.writers import HTTPWriter, KeyedWriterFactory +from cic.token import Token # external imports from cic_types.ext.metadata import MetadataRequestsHandler from cic_types.ext.metadata.signer import Signer as MetadataSigner -# local imports -from cic import ( - Proof, - Processor, - ) -from cic.output import ( - HTTPWriter, - KeyedWriterFactory, - ) -from cic.meta import ( - Meta, - MetadataWriter, - ) -from cic.attachment import Attachment -from cic.network import Network -from cic.token import Token - logg = logging.getLogger(__name__) def process_args(argparser): - argparser.add_argument('-d', '--directory', type=str, dest='directory', default='.', help='directory') - argparser.add_argument('-o', '--output-directory', type=str, dest='output_directory', help='output directory') - argparser.add_argument('--metadata-endpoint', dest='metadata_endpoint', type=str, help='metadata endpoint to interact with') - argparser.add_argument('-y', '--signer', type=str, dest='y', help='target-specific signer to use for export') - argparser.add_argument('-p', type=str, help='RPC endpoint') - argparser.add_argument('target', type=str, help='target network type') + argparser.add_argument( + "-d", "--directory", type=str, dest="directory", default=".", help="directory" + ) + argparser.add_argument( + "-o", + "--output-directory", + type=str, + dest="output_directory", + help="output directory", + ) + argparser.add_argument( + "--metadata-endpoint", + dest="metadata_endpoint", + type=str, + help="metadata endpoint to interact with", + ) + argparser.add_argument( + "-y", + "--signer", + type=str, + dest="y", + help="target-specific signer to use for export", + ) + argparser.add_argument("-p", type=str, help="RPC endpoint") + argparser.add_argument("target", type=str, help="target network type") def validate_args(args): @@ -42,23 +52,37 @@ def validate_args(args): def init_writers_from_config(config): w = { - 'meta': None, - 'attachment': None, - 'proof': None, - 'ext': None, - } + "meta": None, + "attachment": None, + "proof": None, + "ext": None, + } for v in w.keys(): - k = 'CIC_CORE_{}_WRITER'.format(v.upper()) - (d, c) = config.get(k).rsplit('.', maxsplit=1) + k = "CIC_CORE_{}_WRITER".format(v.upper()) + (d, c) = config.get(k).rsplit(".", maxsplit=1) m = importlib.import_module(d) o = getattr(m, c) w[v] = o - + return w -def execute(config, eargs): - modname = 'cic.ext.{}'.format(eargs.target) +ExtraArgs = { + "target": str, + "key_file_path": str, + "gpg_passphrase": str, + "directory": str, + "output_directory": str, + "metadata_endpoint": Optional[str], + "y": str, +} + + +def execute(config, eargs: ExtraArgs): + # !TODO Remove this + eargs.key_file_path = "/home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc" + eargs.gpg_passphrase = "merman" + modname = f"cic.ext.{eargs.target}" cmd_mod = importlib.import_module(modname) writers = init_writers_from_config(config) @@ -66,18 +90,26 @@ def execute(config, eargs): output_writer_path_meta = eargs.output_directory if eargs.metadata_endpoint != None: MetadataRequestsHandler.base_url = eargs.metadata_endpoint - MetadataSigner.gpg_path = os.path.join('/tmp') - MetadataSigner.key_file_path = '/home/lash/src/client/cic/grassrootseconomics/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc' - MetadataSigner.gpg_passphrase = 'merman' - writers['proof'] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new - writers['attachment'] = KeyedWriterFactory(None, HTTPWriter).new - writers['meta'] = MetadataWriter + MetadataSigner.gpg_path = os.path.join("/tmp") + MetadataSigner.key_file_path = eargs.key_file_path + MetadataSigner.gpg_passphrase = eargs.gpg_passphrase + writers["proof"] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new + writers["attachment"] = KeyedWriterFactory(None, HTTPWriter).new + writers["meta"] = MetadataWriter output_writer_path_meta = eargs.metadata_endpoint - + ct = Token(path=eargs.directory) - cm = Meta(path=eargs.directory, writer=writers['meta'](path=output_writer_path_meta)) - ca = Attachment(path=eargs.directory, writer=writers['attachment'](path=output_writer_path_meta)) - cp = Proof(path=eargs.directory, attachments=ca, writer=writers['proof'](path=output_writer_path_meta)) + cm = Meta( + path=eargs.directory, writer=writers["meta"](path=output_writer_path_meta) + ) + ca = Attachment( + path=eargs.directory, writer=writers["attachment"](path=output_writer_path_meta) + ) + cp = Proof( + path=eargs.directory, + attachments=ca, + writer=writers["proof"](path=output_writer_path_meta), + ) cn = Network(path=eargs.directory) ca.load() @@ -88,20 +120,29 @@ def execute(config, eargs): chain_spec = None try: - chain_spec = config.get('CHAIN_SPEC') + chain_spec = config.get("CHAIN_SPEC") except KeyError: chain_spec = cn.chain_spec - config.add(chain_spec, 'CHAIN_SPEC', exists_ok=True) - logg.debug('CHAIN_SPEC config set to {}'.format(str(chain_spec))) + config.add(chain_spec, "CHAIN_SPEC", exists_ok=True) + logg.debug(f"CHAIN_SPEC config set to {str(chain_spec)}") - #signer = cmd_mod.parse_signer(eargs.y) + # signer = cmd_mod.parse_signer(eargs.y) (rpc, signer) = cmd_mod.parse_adapter(config, eargs.y) ref = cn.resource(eargs.target) chain_spec = cn.chain_spec(eargs.target) - logg.debug('found reference {} chain spec {} for target {}'.format(ref['contents'], chain_spec, eargs.target)) - c = getattr(cmd_mod, 'new')(chain_spec, ref['contents'], cp, signer_hint=signer, rpc=rpc, outputs_writer=writers['ext'](path=eargs.output_directory)) + logg.debug( + f"found reference {ref['contents']} chain spec {chain_spec} for target {eargs.target}" + ) + c = getattr(cmd_mod, "new")( + chain_spec, + ref["contents"], + cp, + signer_hint=signer, + rpc=rpc, + outputs_writer=writers["ext"](path=eargs.output_directory), + ) c.apply_token(ct) - p = Processor(proof=cp, attachment=ca, metadata=cm, extensions=[c]) + p = ContractProcessor(proof=cp, attachment=ca, metadata=cm, extensions=[c]) p.process() diff --git a/cic/cmd/ext.py b/cic/cmd/ext.py index 789938d..3f02d73 100644 --- a/cic/cmd/ext.py +++ b/cic/cmd/ext.py @@ -25,6 +25,6 @@ def execute(config, eargs): chain_spec = ChainSpec.from_chain_str(eargs.i) - m = importlib.import_module('cic.ext.{}.start'.format(eargs.target)) + m = importlib.import_module(f'cic.ext.{eargs.target}.start') m.extension_start(cn, registry_address=eargs.registry, chain_spec=chain_spec, rpc_provider=config.get('RPC_PROVIDER')) diff --git a/cic/cmd/wizard.py b/cic/cmd/wizard.py new file mode 100644 index 0000000..82336ba --- /dev/null +++ b/cic/cmd/wizard.py @@ -0,0 +1,390 @@ +from __future__ import annotations + +# standard import +import importlib +import json +import logging +import os +from typing import TYPE_CHECKING, List + +import requests + +# external imports +from chainlib.chain import ChainSpec + +# local imports +from cic import Proof +from cic.actions.deploy import deploy +from cic.actions.types import Contract, Options +from cic.attachment import Attachment +from cic.meta import Meta +from cic.network import Network +from cic.token import Token + +if TYPE_CHECKING: + from chainlib.cli.config import Config + +log = logging.getLogger(__name__) + + +def process_args(argparser): + argparser.add_argument( + "--skip-gen", action="store_true", default=False, help="Skip Generation" + ) + argparser.add_argument( + "--skip-deploy", + action="store_true", + help="Skip Deployment", + ) + argparser.add_argument( + "--target", + default="eth", + help="Contract Target (eth)", + ) + argparser.add_argument( + "path", + type=str, + help="Path to generate/use contract deployment info", + ) + argparser.add_argument( + "-p", + type=str, + help="RPC Provider (http://localhost:8545)", + ) + argparser.add_argument( + "-y", + type=str, + help="Wallet Keystore", + ) + + +def validate_args(_args): + pass + + +CONTRACTS = [ + { + "url": "https://gitlab.com/cicnet/eth-erc20/-/raw/master/python/giftable_erc20_token/data/GiftableToken", + "name": "Giftable Token", + }, + { + "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", + "name": "Demurrage Token Single No Cap", + }, +] + +# Download File from Url +def download_file(url: str, directory: str, filename=None) -> (str, bytes): + os.makedirs(directory, exist_ok=True) + filename = filename if filename else url.split("/")[-1] + path = os.path.join(directory, filename) + if not os.path.exists(path): + log.debug(f"Downloading {filename}") + r = requests.get(url, allow_redirects=True) + open(path, "wb").write(r.content) + return path + return path + + +def get_contract_args(data: list): + for item in data: + if item["type"] == "constructor": + return item["inputs"] + raise Exception("No constructor found in contract") + + +def print_contract_args(json_path: str): + json_data = json.load(open(json_path, encoding="utf-8")) + print("Contract Args:") + for contract_arg in get_contract_args(json_data): + print( + f"\t{contract_arg.get('name', '')} - {contract_arg.get('type', '')}" + ) + + +def select_contract(): + print("Contracts:") + print("\t C - Custom (path/url to contract)") + for idx, contract in enumerate(CONTRACTS): + print(f"\t {idx} - {contract['name']}") + + val = input("Select contract (C,0,1..): ") + if val.isdigit() and int(val) < len(CONTRACTS): + contract = CONTRACTS[int(val)] + directory = f"./contracts/{contract['name']}" + bin_path = os.path.abspath(download_file(contract["url"] + ".bin", directory)) + json_path = download_file(contract["url"] + ".json", directory) + elif val == "C": + possible_bin_location = input("Enter path/url to contract: ") + # possible_bin_location is path + if possible_bin_location[0] == "." or possible_bin_location[0] == "/": + if os.path.exists(possible_bin_location): + bin_path = os.path.abspath(possible_bin_location) + else: + raise Exception(f"File {possible_bin_location} does not exist") + + possible_json_path = val.replace(".bin", ".json") + if os.path.exists(possible_json_path): + json_path = possible_json_path + # possible_bin_location is url + else: + bin_path = download_file(possible_bin_location, directory) + else: + print("Invalid selection") + exit(1) + contract_extra_args = [] + contract_extra_args_types = [] + + if os.path.exists(json_path): + json_data = json.load(open(json_path, encoding="utf-8")) + for contract_arg in get_contract_args(json_data): + arg_name = contract_arg.get("name") + arg_type = contract_arg.get("type") + if arg_name not in ["_decimals", "_name", "_symbol"]: + val = input(f"Enter value for {arg_name} ({arg_type}): ") + contract_extra_args.append(val) + if arg_type == "uint128": + contract_extra_args_types.append("uint256") + else: + contract_extra_args_types.append(arg_type) + + return { + "bin_path": bin_path, + "json_path": json_path, + "extra_args": contract_extra_args, + "extra_args_types": contract_extra_args_types, + } + + +def init_token(directory: str, code=""): + contract = select_contract() + code = contract["bin_path"] + contract_extra_args = contract["extra_args"] + contract_extra_args_types = contract["extra_args_types"] + + name = input("Enter Token Name (Foo Token): ") or "Foo Token" + symbol = input("Enter Token Symbol (FOO): ") or "FOO" + precision = input("Enter Token Precision (6): ") or 6 + supply = input("Enter Token Supply (0): ") or 0 + + contract_token = Token( + directory, + name=name, + symbol=symbol, + precision=precision, + extra_args=contract_extra_args, + extra_args_types=contract_extra_args_types, + supply=supply, + code=code, + ) + contract_token.start() + return contract_token + + +def init_proof(directory): + description = input("Enter Proof Description (None): ") or None + namespace = input("Enter Proof Namespace (ge): ") or "ge" + issuer = input("Enter Proof Issuer (None): ") or None + contract_proof = Proof(directory, description, namespace, issuer) + contract_proof.start() + return contract_proof + + +def init_meta(directory): + name = input("Enter Name (None): ") or "" + country_code = input("Enter Country Code (KE): ") or "KE" + location = input("Enter Location (None): ") or "" + adding_contact_info = True + contact = {} + while adding_contact_info: + value = input("Enter contact info (e.g 'phone: +254723522718'): ") or None + if value: + data = value.split(":") + if len(data) != 2: + print("Invalid contact info, you must enter in the format 'key: value'") + continue + contact[data[0].strip()] = data[1].strip() + else: + adding_contact_info = False + contract_meta = Meta( + directory, + name=name, + country_code=country_code, + location=location, + contact=contact, + ) + contract_meta.start() + return contract_meta + + +def init_attachment(directory): + contract_attchment = Attachment(directory) + contract_attchment.start() + input( + f"Please add attachment files to '{os.path.abspath(os.path.join(directory,'attachments'))}' and then press ENTER to continue" + ) + contract_attchment.load() + return contract_attchment + + +def load_contract(directory) -> Contract: + token = Token(path=directory) + proof = Proof(path=directory) + meta = Meta(path=directory) + attachment = Attachment(path=directory) + network = Network(directory) + + token.load() + proof.load() + meta.load() + attachment.load() + network.load() + return Contract( + token=token, proof=proof, meta=meta, attachment=attachment, network=network + ) + + +def init_network( + directory, + options: Options, + targets: List[str], +): + contract_network = Network(directory, targets=targets) + contract_network.start() + + for target in targets: + m = importlib.import_module(f"cic.ext.{target}.start") + m.extension_start( + contract_network, + registry_address=options.contract_registry, + chain_spec=options.chain_spec, + rpc_provider=options.rpc_provider, + key_account_address=options.key_account, + ) + contract_network.load() + return contract_network + + +def generate(directory: str, target: str, options: Options) -> Contract: + if os.path.exists(directory): + contine = input( + "Directory already exists, Would you like to delete it? (y/n): " + ) + if contine.lower() != "y": + print("Exiting") + exit(1) + else: + print(f"Deleted {directory}") + os.system(f"rm -rf {directory}") + os.makedirs(directory) + + token = init_token(directory) + proof = init_proof(directory) + meta = init_meta(directory) + attachment = init_attachment(directory) + network = init_network( + directory, + options, + targets=[target], + ) + return Contract( + token=token, proof=proof, meta=meta, attachment=attachment, network=network + ) + + +def get_options(config: Config, eargs) -> Options: + # Defaults + default_contract_registry = config.get( + "CIC_REGISTRY_ADDRESS" + ) # Comes from /home/will/grassroots/cic-staff-installer/var/cic-staff-client/CIC_REGISTRY_ADDRESS + default_key_account = config.get("AUTH_KEY") + # https://meta.grassrootseconomics.net + # https://auth.grassrootseconomics.net Authenticated Meta + + default_metadata_endpoint = config.get("META_URL") + # Keyring folder needs to be dumped out as a private key file from $HOME/.config/cic/staff-client/.gnupg + default_wallet_keyfile = eargs.y or config.get( + "WALLET_KEY_FILE" + ) # Show possible wallet keys + + # Should be an input??? + default_wallet_passphrase = config.get("WALLET_PASSPHRASE", "merman") + default_chain_spec = config.get("CHAIN_SPEC") + default_rpc_provider = config.get("RPC_PROVIDER") + + contract_registry = ( + input(f"Enter Contract Registry ({default_contract_registry}): ") + or default_contract_registry + ) + rpc_provider = ( + input(f"Enter RPC Provider ({default_rpc_provider}): ") or default_rpc_provider + ) + chain_spec = ChainSpec.from_chain_str( + (input(f"Enter ChainSpec ({default_chain_spec}): ") or default_chain_spec) + ) + key_account = ( + input(f"Enter KeyAccount ({default_key_account}): ") or default_key_account + ) + metadata_endpoint = ( + input(f"Enter Metadata Endpoint ({default_metadata_endpoint}): ") + or default_metadata_endpoint + ) + auth_passphrase = config.get("AUTH_PASSPHRASE") + auth_keyfile_path = config.get("AUTH_KEYFILE_PATH") + auth_db_path = config.get("AUTH_DB_PATH") + + options = Options( + auth_db_path, + auth_keyfile_path, + auth_passphrase, + contract_registry, + key_account, + chain_spec, + rpc_provider, + metadata_endpoint, + default_wallet_keyfile, + default_wallet_passphrase, + ) + print(options) + return options + + + + + +ExtraArgs = {"skip_gen": str, "skip_deploy": str, "target": str, "path": str, "p": str} + + +def execute(config, eargs: ExtraArgs): + print(f"eargs: {eargs}") + directory = eargs.path + target = eargs.target + skip_gen = eargs.skip_gen + skip_deploy = eargs.skip_deploy + + options = get_options(config, eargs) + + if not skip_gen: + contract = generate(directory, target, options) + else: + contract = load_contract(directory) + + print_contract(contract) + + if not skip_deploy: + ready_to_deploy = input("Ready to deploy? (y/n): ") + if ready_to_deploy == "y": + deploy( + config=config, + contract_directory=directory, + options=options, + target=target, + ) + print("Deployed") + else: + print("Not deploying") + + +if __name__ == "__main__": + # execute() + print("Not Implemented") diff --git a/cic/contract/__init__.py b/cic/contract/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cic/base.py b/cic/contract/base.py similarity index 100% rename from cic/base.py rename to cic/contract/base.py diff --git a/cic/contract/components/__init__.py b/cic/contract/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cic/attachment.py b/cic/contract/components/attachment.py similarity index 52% rename from cic/attachment.py rename to cic/contract/components/attachment.py index 2f97609..c18c0a6 100644 --- a/cic/attachment.py +++ b/cic/contract/components/attachment.py @@ -1,9 +1,9 @@ # standard imports -import os import logging +import os # local imports -from .base import * +from cic.contract.base import Data, data_dir logg = logging.getLogger(__name__) @@ -14,38 +14,39 @@ class Attachment(Data): :param path: Path to settings directory :type path: str :param writer: Writer interface receiving the output of the processor - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter """ - def __init__(self, path='.', writer=None): + + def __init__(self, path=".", writer=None, interactive=False): super(Attachment, self).__init__() self.contents = {} self.path = path self.writer = writer - self.attachment_path = os.path.join(self.path, 'attachments') - + self.attachment_path = os.path.join(self.path, "attachments") + if interactive: + self.start() + input( + f"Please add attachment files to '{os.path.abspath(os.path.join(self.path,'attachments'))}' and then press ENTER to continue" + ) + self.load() def load(self): - """Loads attachment data from settings. - """ + """Loads attachment data from settings.""" for s in os.listdir(self.attachment_path): fp = os.path.realpath(os.path.join(self.attachment_path, s)) - f = open(fp, 'rb') - r = f.read() - f.close() + with open(fp, "rb") as f: + r = f.read() z = self.hash(r).hex() self.contents[z] = fp - logg.debug('loaded attachment file {} digest {}'.format(fp, z)) - + logg.debug(f"loaded attachment file {fp} digest {z}") def start(self): - """Initialize attachment settings from template. - """ + """Initialize attachment settings from template.""" super(Attachment, self).start() os.makedirs(self.attachment_path) - def get(self, k): """Get a single attachment by the sha256 hash of the content. @@ -54,33 +55,28 @@ class Attachment(Data): """ return self.contents[k] - def asdict(self): - """Output attachment state to dict - """ + """Output attachment state to dict""" return self.contents - def process(self, token_address=None, token_symbol=None, writer=None): """Serialize and publish attachments. - See cic.processor.Processor.process + See cic.processor.Processor.process """ if writer == None: writer = self.writer - for k in self.contents.keys(): - fp = os.path.join(self.attachment_path, self.contents[k]) - f = open(fp, 'rb') - v = f.read() - f.close() - logg.debug('writing attachment {}'.format(k)) - writer.write(k, v) - + for key, value in self.contents.items(): + fp = os.path.join(self.attachment_path, value) + with open(fp, "rb") as f: + data = f.read() + logg.debug(f"writing attachment {key}") + writer.write(key, data) def __str__(self): - s = '' - for k in self.contents.keys(): - s += '{} = {}\n'.format(k, self.contents[k]) #self.digests[i].hex(), self.contents[i]) + s = "" + for key, value in self.contents.items(): + s += f"{key} = {value}\n" # self.digests[i].hex(), self.contents[i]) return s diff --git a/cic/contract/components/meta.py b/cic/contract/components/meta.py new file mode 100644 index 0000000..20e1bab --- /dev/null +++ b/cic/contract/components/meta.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +# standard imports +import os +import json +import logging +import base64 +from typing import TYPE_CHECKING + +# external imports +from cic_types import MetadataPointer +from cic_types.processor import generate_metadata_pointer +from hexathon import strip_0x + +# local imports +from cic.contract.base import Data, data_dir +from cic.writers import OutputWriter +from cic_types.ext.metadata import MetadataRequestsHandler +from cic.utils import object_to_str + +logg = logging.getLogger(__name__) + + +class Meta(Data): + """Serialize and publish metadata for token. + + The token metadata is any mutable data that is not part of the initial token proof, but published simultaneously as the token nonetheless. + + :param path: Path to settings directory + :type path: str + :param writer: Writer interface receiving the output of the processor + :type writer: cic.writers.OutputWriter + """ + + def __init__( + self, path=".", writer=None, name="", location="", country_code="KE", contact={}, interactive=False + ): + super(Meta, self).__init__() + self.name = name + self.contact = contact + self.country_code = country_code + self.location = location + self.path = path + self.writer = writer + self.meta_path = os.path.join(self.path, "meta.json") + + if interactive: + self.name = input(f"Enter Metadata Name ({self.name}): ") or self.name + self.country_code = input(f"Enter Metadata Country Code ({self.country_code}): ") or self.country_code + self.location = input(f"Enter Metadata Location ({self.location}): ") or self.location + + adding_contact_info = True + contact = {} + while adding_contact_info: + value = input("Enter Metadata contact info (e.g 'phone: +254723522718'): ") or None + if value: + data = value.split(":") + if len(data) != 2: + print("Invalid contact info, you must enter in the format 'key: value'") + continue + contact[data[0].strip()] = data[1].strip() + else: + adding_contact_info = False + self.contact = contact + + def load(self): + """Load metadata from settings.""" + super(Meta, self).load() + + f = open(self.meta_path, "r", encoding="utf-8") + o = json.load(f) + f.close() + + self.name = o["name"] + self.contact = o["contact"] + self.country_code = o["country_code"] + self.location = o["location"] + self.inited = True + + def start(self): + """Initialize metadata settings from template.""" + super(Meta, self).start() + + meta_template_file_path = os.path.join( + data_dir, f"meta_template_v{self.version()}.json" + ) + + f = open(meta_template_file_path, encoding="utf-8") + o = json.load(f) + f.close() + + o["name"] = self.name + o["contact"] = self.contact + o["country_code"] = self.country_code + o["location"] = self.location + + f = open(self.meta_path, "w", encoding="utf-8") + json.dump(o, f, sort_keys=True, indent="\t") + f.close() + + def reference(self, token_address): + """Calculate the mutable reference for the token metadata.""" + token_address_bytes = bytes.fromhex(strip_0x(token_address)) + return generate_metadata_pointer( + token_address_bytes, MetadataPointer.TOKEN_META + ) + + def asdict(self): + """Output proof state to dict.""" + return { + "name": self.name, + "country_code": self.country_code, + "location": self.location, + "contact": self.contact, + } + + def process(self, token_address=None, token_symbol=None, writer=None): + """Serialize and publish metadata. + + See cic.processor.Processor.process + """ + if writer is None: + writer = self.writer + + v = json.dumps(self.asdict(), separators=(",", ":")) + + token_address_bytes = bytes.fromhex(strip_0x(token_address)) + k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_META) + writer.write(k, v.encode("utf-8")) + + token_symbol_bytes = token_symbol.encode("utf-8") + k = generate_metadata_pointer( + token_symbol_bytes, MetadataPointer.TOKEN_META_SYMBOL + ) + writer.write(k, v.encode("utf-8")) + + return (k, v) + + def __str__(self): + return object_to_str(self, ["name", "contact", "country_code", "location"]) + + +class MetadataWriter(OutputWriter): + """Custom writer for publishing data under immutable content-addressed pointers in the cic-meta storage backend. + + Data that is not utf-8 will be converted to base64 before publishing. + + Implements cic.writers.OutputWriter + """ + + def write(self, k, v): + rq = MetadataRequestsHandler(MetadataPointer.NONE, bytes.fromhex(k)) + try: + v = v.decode("utf-8") + v = json.loads(v) + logg.debug(f"metadatawriter bindecode {k} {v}") + except UnicodeDecodeError: + v = base64.b64encode(v).decode("utf-8") + v = json.loads(json.dumps(v, separators=(",", ":"))) + logg.debug(f"metadatawriter b64encode {k} {v}") + r = rq.create(v) + logg.info(f"metadata submitted at {k}") + return r diff --git a/cic/network.py b/cic/contract/components/network.py similarity index 88% rename from cic/network.py rename to cic/contract/components/network.py index baf0f93..c77bb46 100644 --- a/cic/network.py +++ b/cic/contract/components/network.py @@ -7,10 +7,7 @@ import logging from chainlib.chain import ChainSpec # local imports -from .base import ( - Data, - data_dir, - ) +from cic.contract.components.base import Data, data_dir logg = logging.getLogger(__name__) @@ -54,7 +51,7 @@ class Network(Data): """ super(Network, self).load() - network_template_file_path = os.path.join(data_dir, 'network_template_v{}.json'.format(self.version())) + network_template_file_path = os.path.join(data_dir, f'network_template_v{self.version()}.json') f = open(network_template_file_path) o_part = json.load(f) @@ -138,11 +135,11 @@ class Network(Data): def __str__(self): s = '' - for k in self.resources.keys(): - for kk in self.resources[k]['contents'].keys(): - v = self.resources[k]['contents'][kk] - if v == None: - v = '' - s += '{}.{} = {}\n'.format(k, kk, v) + for resource in self.resources.keys(): + for content_key in self.resources[resource]['contents'].keys(): + content_value = self.resources[resource]['contents'][content_key] + if content_value == None: + content_value = '' + s += f'{resource}.{content_key} = {content_value}\n' return s diff --git a/cic/contract/components/proof.py b/cic/contract/components/proof.py new file mode 100644 index 0000000..7bf923c --- /dev/null +++ b/cic/contract/components/proof.py @@ -0,0 +1,192 @@ +# standard imports +import json +import logging +import os +import tempfile + +# external imports +from hexathon import strip_0x +from cic_types import MetadataPointer +from cic_types.processor import generate_metadata_pointer + +# local imports +from cic.contract.base import Data, data_dir +from cic.utils import object_to_str + + +logg = logging.getLogger(__name__) + + +class Proof(Data): + """Proof handles the immutable token proof data mapped to the initial token deployment. + + It processes inputs from the proof.json file in the session directory. + + Optionally, attachment objects can be added to the proof. If added, the resulting proof digest will consists of the attachment digests added to the root digest. These are then are deterministically ordered, regardless of which order attachments were given to the constructor. + + :param path: Path to settings directory + :type path: str + :param attachments: List of attachment objects to include in the proof + :type attachments: cic.attachment.Attachment + :param writer: Writer interface receiving the output of the processor + :type writer: cic.writers.OutputWriter + """ + + def __init__( + self, + path=".", + description="", + namespace="ge", + issuer="", + attachments=None, + writer=None, + interactive=False, + ): + super(Proof, self).__init__() + self.proofs = [] + self.namespace = namespace + self.description = description + self.issuer = issuer + self.path = path + self.writer = writer + self.extra_attachments = attachments + self.attachments = {} + self.proof_path = os.path.join(self.path, "proof.json") + self.temp_proof_path = tempfile.mkstemp()[1] + + if interactive: + self.description = ( + input(f"Enter Proof Description ({self.description}): ") or self.description + ) + self.namespace = ( + input(f"Enter Proof Namespace ({self.namespace}): ") or self.namespace + ) + self.issuer = input(f"Enter Proof Issuer ({self.issuer}): ") or self.issuer + + def load(self): + """Load proof data from settings.""" + super(Proof, self).load() + + f = open(self.proof_path, "r") + o = json.load(f) + f.close() + + self.set_version(o["version"]) + self.description = o["description"] + self.namespace = o["namespace"] + self.issuer = o["issuer"] + self.proofs = o["proofs"] + + if self.extra_attachments != None: + a = self.extra_attachments.asdict() + for k in a.keys(): + self.attachments[k] = a[k] + + hshs = self.__get_ordered_hashes() + self.proofs = list(map(strip_0x, hshs)) + + self.inited = True + + def start(self): + """Initialize proof settings from template.""" + super(Proof, self).start() + + proof_template_file_path = os.path.join( + data_dir, f"proof_template_v{self.version()}.json" + ) + + with open(proof_template_file_path, "r", encoding="utf-8") as f: + o = json.load(f) + + o["issuer"] = self.issuer + o["description"] = self.description + o["namespace"] = self.namespace + + with open(self.proof_path, "w", encoding="utf-8") as f: + json.dump(o, f, sort_keys=True, indent="\t") + + def asdict(self): + """Output proof state to dict.""" + return { + "version": self.version(), + "namespace": self.namespace, + "description": self.description, + "issuer": self.issuer, + "proofs": self.proofs, + } + + # TODO: the efficiency of this method could probably be improved. + def __get_ordered_hashes(self): + ks = list(self.attachments.keys()) + ks.sort() + + return ks + + # def get(self): + # hsh = self.hash(b).hex() + # self.attachments[hsh] = self.temp_proof_path + # logg.debug('cbor of {} is {} hashes to {}'.format(v, b.hex(), hsh)) + + def root(self): + """Calculate the root digest from the serialized proof object.""" + v = self.asdict() + # b = cbor2.dumps(v) + b = json.dumps(v, separators=(",", ":")) + + with open(self.temp_proof_path, "w", encoding="utf-8") as f: + f.write(b) + + b = b.encode("utf-8") + k = self.hash(b) + + return (k.hex(), b) + + def process(self, token_address=None, token_symbol=None, writer=None): + """Serialize and publish proof. + + See cic.processor.Processor.process + """ + if writer is None: + writer = self.writer + + (k, v) = self.root() + writer.write(k, v) + root_key = k + + token_symbol_bytes = token_symbol.encode("utf-8") + k = generate_metadata_pointer( + token_symbol_bytes, MetadataPointer.TOKEN_PROOF_SYMBOL + ) + writer.write(k, v) + + token_address_bytes = bytes.fromhex(strip_0x(token_address)) + k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_PROOF) + writer.write(k, v) + + # (hsh, hshs) = self.get() + # hshs = list(map(strip_0x, hshs)) + # hshs_bin = list(map(bytes.fromhex, hshs)) + # hshs_cat = b''.join(hshs_bin) + + # f = open(self.temp_proof_path, 'rb') + # v = f.read() + # f.close() + # writer.write(hsh, v) + + # r = self.hash(hshs_cat) + # r_hex = r.hex() + + # logg.debug('generated proof {} for hashes {}'.format(r_hex, hshs)) + + # writer.write(r_hex, hshs_cat) + + o = self.asdict() + with open(self.proof_path, "w", encoding="utf-8") as f: + json.dump(o, f, sort_keys=True, indent="\t") + + return root_key + + def __str__(self): + return object_to_str( + self, ["description", "issuer", "namespace", "version()", "proofs"] + ) diff --git a/cic/contract/components/token.py b/cic/contract/components/token.py new file mode 100644 index 0000000..156be8a --- /dev/null +++ b/cic/contract/components/token.py @@ -0,0 +1,123 @@ +# standard imports +import json +import os + +# local imports +from cic.contract.base import Data, data_dir +from cic.contract.helpers import select_contract + + +class Token(Data): + """Encapsulates the token data used by the extension to deploy and/or register token and token related applications on chain. + + Token details (name, symbol etc) will be used to initialize the token settings when start is called. If load is called instead, any token detail parameters passed to the constructor will be overwritten by data stored in the settings. + + :param path: Settings directory path + :type path: str + :param name: Token name + :type name: str + :param symbol: Token symbol + :type symbol: str + :param precision: Token value precision (number of decimals) + :type precision: int + :param supply: Token supply (in smallest precision units) + :type supply: int + :param code: Bytecode for token chain application + :type code: str (hex) + """ + + def __init__( + self, + path=".", + name="Foo Token", + symbol="FOO", + precision=6, + supply=0, + code=None, + extra_args=[], + extra_args_types=[], + interactive=False, + ): + super(Token, self).__init__() + self.name = name + self.symbol = symbol + self.supply = supply + self.precision = precision + self.code = code + self.extra_args = extra_args + self.extra_args_types = extra_args_types + self.path = path + self.token_path = os.path.join(self.path, "token.json") + + if interactive: + contract = select_contract() + self.code = contract["bin_path"] + self.extra_args = contract["extra_args"] + self.extra_args_types = contract["extra_args_types"] + + self.name = input(f"Enter Token Name ({self.name}): ") or self.name + self.symbol = input(f"Enter Token Symbol ({self.symbol}): ") or self.symbol + self.precision = input(f"Enter Token Precision ({self.precision}): ") or self.precision + self.supply = input(f"Enter Token Supply ({self.supply}): ") or self.supply + + def load(self): + """Load token data from settings.""" + super(Token, self).load() + + with open(self.token_path, "r", encoding="utf-8") as f: + o = json.load(f) + + self.name = o["name"] + self.symbol = o["symbol"] + self.precision = o["precision"] + self.code = o["code"] + self.supply = o["supply"] + extras = [] + extra_types = [] + token_extras: list = o["extra"] + if token_extras: + for idx, token_extra in enumerate(token_extras): + arg = token_extra.get("arg") + arg_type = token_extra.get("arg_type") + if arg and arg_type: + extras.append(arg) + extra_types.append(arg_type) + elif (arg and not arg_type) or (not arg and arg_type): + raise ValueError( + f"Extra contract args must have a 'arg' and 'arg_type', Please check {self.token_path}:extra[{idx}] " + ) + self.extra_args = extras + self.extra_args_types = extra_types + self.inited = True + + def start(self): + """Initialize token settings from arguments passed to the constructor and/or template.""" + super(Token, self).load() + + token_template_file_path = os.path.join( + data_dir, f"token_template_v{self.version()}.json" + ) + with open(token_template_file_path, encoding="utf-8") as f: + o = json.load(f) + o["name"] = self.name + o["symbol"] = self.symbol + o["precision"] = self.precision + o["code"] = self.code + o["supply"] = self.supply + extra = [] + for idx, extra_arg in enumerate(self.extra_args): + extra.append({"arg": extra_arg, "arg_type": self.extra_args_types[idx]}) + if len(extra) != 0: + o["extra"] = extra + + with open(self.token_path, "w", encoding="utf-8") as f: + json.dump(o, f, sort_keys=True, indent="\t") + + def __str__(self): + s = f"name = {self.name}\n" + s += f"symbol = {self.symbol}\n" + s += f"precision = {self.precision}\n" + s += f"supply = {self.supply}\n" + for idx, extra in enumerate(self.extra_args): + s += f"extra_args[{idx}]({self.extra_args_types[idx]}) = {extra}\n" + return s diff --git a/cic/contract/contract.py b/cic/contract/contract.py new file mode 100644 index 0000000..b11fce0 --- /dev/null +++ b/cic/contract/contract.py @@ -0,0 +1,191 @@ +# Standard +import importlib +import json +import logging +import os +from typing import List, TYPE_CHECKING +import requests + +# external imports +from cic_types.ext.metadata import MetadataRequestsHandler +from cic_types.ext.metadata.signer import Signer as MetadataSigner +from chainlib.cli.config import Config + +# Local Modules +from cic.contract import ContractProcessor +from cic.contract.components.attachment import Attachment +from cic.contract.components.meta import Meta +from cic.contract.components.network import Network +from cic.contract.components.proof import Proof +from cic.contract.components.token import Token +from cic.contract.helpers import init_writers_from_config +from cic.writers import HTTPWriter, KeyedWriterFactory, OutputWriter + + +log = logging.getLogger(__name__) + + +class Contract: + """ """ + + def __init__( + self, + token: Token, + proof: Proof, + meta: Meta, + attachment: Attachment, + network: Network, + ): + self.token = token + self.proof = proof + self.meta = meta + self.attachment = attachment + self.network = network + + def __str__(self): + s = "" + s += f"[cic.header]\nversion = {self.proof.version()}\n" + s += f"[cic.token]\n{self.token}" + s += f"[cic.proof]\n{self.proof}" + s += f"[cic.meta]\n{self.meta}" + s += f"[cic.attachment]\n{self.attachment}" + s += f"[cic.network]\n{self.network}" + return s + + +def load_contract(directory) -> Contract: + token = Token(path=directory) + proof = Proof(path=directory) + meta = Meta(path=directory) + attachment = Attachment(path=directory) + network = Network(directory) + + token.load() + proof.load() + meta.load() + attachment.load() + network.load() + return Contract( + token=token, proof=proof, meta=meta, attachment=attachment, network=network + ) + + +def generate_contract( + directory: str, targets: List[str], config, interactive=True +) -> Contract: + if os.path.exists(directory): + contine = input( + "Directory already exists, Would you like to delete it? (y/n): " + ) + if contine.lower() != "y": + print("Exiting") + exit(1) + else: + print(f"Deleted {directory}") + os.system(f"rm -rf {directory}") + os.makedirs(directory) + + token = Token(directory, interactive=interactive) + token.start() + + proof = Proof(directory, interactive=interactive) + proof.start() + + meta = Meta(directory, interactive=interactive) + meta.start() + + attachment = Attachment(directory, interactive=interactive) + + network = Network(directory, targets=targets) + network.start() + + for target in targets: + m = importlib.import_module(f"cic.ext.{target}.start") + m.extension_start( + network, + registry_address=config.get("CIC_REGISTRY_ADDRESS"), + chain_spec=config.get("CHAIN_SPEC"), + rpc_provider=config.get("RPC_PROVIDER"), + key_account_address=config.get("RPC_PROVIDER"), + ) + network.load() + + return Contract( + token=token, proof=proof, meta=meta, attachment=attachment, network=network + ) + + +def deploy( + config: Config, + target: str, + contract_directory: str, +): + + modname = f"cic.ext.{target}" + cmd_mod = importlib.import_module(modname) + + writers = init_writers_from_config(config) + output_directory = os.path.join(contract_directory, "out") + output_writer_path_meta = output_directory + + metadata_endpoint = config.get("META_URL") + + if metadata_endpoint is not None: + MetadataRequestsHandler.base_url = metadata_endpoint + MetadataSigner.gpg_path = "/tmp" + MetadataSigner.key_file_path = config.get("AUTH_KEYFILE") + MetadataSigner.gpg_passphrase = config.get("AUTH_PASSPHRASE") + writers["proof"] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new + writers["attachment"] = KeyedWriterFactory(None, HTTPWriter).new + writers["meta"] = MetadataWriter + output_writer_path_meta = metadata_endpoint + + ct = Token(path=contract_directory) + cm = Meta( + path=contract_directory, writer=writers["meta"](path=output_writer_path_meta) + ) + ca = Attachment( + path=contract_directory, + writer=writers["attachment"](path=output_writer_path_meta), + ) + cp = Proof( + path=contract_directory, + attachments=ca, + writer=writers["proof"](path=output_writer_path_meta), + ) + cn = Network(path=contract_directory) + + ca.load() + ct.load() + cp.load() + cm.load() + cn.load() + + chain_spec = None + try: + chain_spec = config.get("CHAIN_SPEC") + log.debug(f"using CHAIN_SPEC from config: {chain_spec}") + except KeyError: + chain_spec = cn.chain_spec + config.add(chain_spec, "CHAIN_SPEC", exists_ok=True) + log.debug(f"using CHAIN_SPEC: {str(chain_spec)} from network") + signer_hint = config.get("WALLET_KEY_FILE") + (rpc, signer) = cmd_mod.parse_adapter(config, signer_hint) + + target_network_reference = cn.resource(target) + chain_spec = cn.chain_spec(target) + log.debug( + f'found reference {target_network_reference["contents"]} chain spec {chain_spec} for target {target}' + ) + c = getattr(cmd_mod, "new")( + chain_spec, + target_network_reference["contents"], + cp, + signer_hint=signer, + rpc=rpc, + outputs_writer=writers["ext"](path=output_directory), + ) + c.apply_token(ct) + + p = ContractProcessor(proof=cp, attachment=ca, metadata=cm, extensions=[c]) + p.process() diff --git a/cic/contract/helpers.py b/cic/contract/helpers.py new file mode 100644 index 0000000..93b65d1 --- /dev/null +++ b/cic/contract/helpers.py @@ -0,0 +1,120 @@ +# standard imports +import os +import logging +import sys +import json +import requests + +# local imports +from cic.writers import OutputWriter + +log = logging.getLogger(__name__) + + +CONTRACTS = [ + { + "url": "https://gitlab.com/cicnet/eth-erc20/-/raw/master/python/giftable_erc20_token/data/GiftableToken", + "name": "Giftable Token", + }, + { + "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", + "name": "Demurrage Token Single No Cap", + }, +] + +# Download File from Url +def download_file(url: str, directory: str, filename=None) -> (str, bytes): + os.makedirs(directory, exist_ok=True) + filename = filename if filename else url.split("/")[-1] + path = os.path.join(directory, filename) + if not os.path.exists(path): + log.debug(f"Downloading {filename}") + r = requests.get(url, allow_redirects=True) + open(path, "wb").write(r.content) + return path + return path + +def get_contract_args(data: list): + for item in data: + if item["type"] == "constructor": + return item["inputs"] + raise Exception("No constructor found in contract") + +def select_contract(): + print("Contracts:") + print("\t C - Custom (path/url to contract)") + for idx, contract in enumerate(CONTRACTS): + print(f"\t {idx} - {contract['name']}") + + val = input("Select contract (C,0,1..): ") + if val.isdigit() and int(val) < len(CONTRACTS): + contract = CONTRACTS[int(val)] + directory = f"./contracts/{contract['name']}" + bin_path = os.path.abspath(download_file(contract["url"] + ".bin", directory)) + json_path = download_file(contract["url"] + ".json", directory) + + elif val == "C": + possible_bin_location = input("Enter a path or url to a contract.bin: ") + if possible_bin_location.startswith('http'): + # possible_bin_location is url + bin_path = download_file(possible_bin_location, directory) + else: + # possible_bin_location is path + if os.path.exists(possible_bin_location): + bin_path = os.path.abspath(possible_bin_location) + else: + raise Exception(f"File {possible_bin_location} does not exist") + + possible_json_path = val.replace(".bin", ".json") + if os.path.exists(possible_json_path): + json_path = possible_json_path + else: + print("Invalid selection") + sys.exit(1) + contract_extra_args = [] + contract_extra_args_types = [] + + if os.path.exists(json_path): + with open(json_path, encoding="utf-8") as f: + json_data = json.load(f) + for contract_arg in get_contract_args(json_data): + arg_name = contract_arg.get("name") + arg_type = contract_arg.get("type") + if arg_name not in ["_decimals", "_name", "_symbol"]: + val = input(f"Enter value for {arg_name} ({arg_type}): ") + contract_extra_args.append(val) + if arg_type == "uint128": + contract_extra_args_types.append("uint256") + else: + contract_extra_args_types.append(arg_type) + + return { + "bin_path": bin_path, + "json_path": json_path, + "extra_args": contract_extra_args, + "extra_args_types": contract_extra_args_types, + } + + +Writers = { + "meta": OutputWriter, + "attachment": OutputWriter, + "proof": OutputWriter, + "ext": OutputWriter, +} + +def init_writers_from_config(config) -> Writers: + writers: Writers = { + "meta": None, + "attachment": None, + "proof": None, + "ext": None, + } + for key in writers: + writer_config_name = f"CIC_CORE_{key.upper()}_WRITER" + (module_name, attribute_name) = config.get(writer_config_name).rsplit(".", maxsplit=1) + mod = importlib.import_module(module_name) + writer = getattr(mod, attribute_name) + writers[key] = writer + + return writers diff --git a/cic/processor.py b/cic/contract/processor.py similarity index 71% rename from cic/processor.py rename to cic/contract/processor.py index c5e4399..c95238e 100644 --- a/cic/processor.py +++ b/cic/contract/processor.py @@ -4,7 +4,7 @@ import logging logg = logging.getLogger(__name__) -class Processor: +class ContractProcessor: """Drives the serialization and publishing of contracts, proofs and metadata for the token. :param proof: Proof object to publish @@ -14,31 +14,37 @@ class Processor: :param metadata: Metadata object to publish :type metadata: cic.meta.Meta :param writer: Writer interface receiving the output of the processor - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter :param extensions: Extension contexts to publish to :type extensions: list of cic.extension.Extension """ - def __init__(self, proof=None, attachment=None, metadata=None, outputs_writer=None, extensions=[]): + + def __init__( + self, + proof=None, + attachment=None, + metadata=None, + outputs_writer=None, + extensions=[], + ): self.token_address = None self.extensions = extensions self.cores = { - 'metadata': metadata, - 'attachment': attachment, - 'proof': proof, - } + "metadata": metadata, + "attachment": attachment, + "proof": proof, + } self.outputs = [] self.__outputs_writer = outputs_writer - def writer(self): """Return the writer instance that the process is using. - :rtype: cic.output.OutputWriter + :rtype: cic.writers.OutputWriter :return: Writer """ return self.__outputs_writer - def get_outputs(self): """Return all written outputs. @@ -53,7 +59,6 @@ class Processor: outputs += self.outputs return outputs - def process(self, writer=None): """Serializes and publishes all token data. @@ -62,22 +67,26 @@ class Processor: All output written to the publish writer will also be cached so that it subsequently be recalled using the get_outputs method. :param writer: Writer to use for publishing. - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter """ tasks = [ - 'attachment', - 'proof', - 'metadata', - ] + "attachment", + "proof", + "metadata", + ] for ext in self.extensions: (token_address, token_symbol) = ext.process() for task in tasks: a = self.cores.get(task) - if a == None: - logg.debug('skipping missing task receiver "{}"'.format(task)) + if a is None: + logg.debug(f'skipping missing task receiver "{task}"') continue - v = a.process(token_address=token_address, token_symbol=token_symbol, writer=self.__outputs_writer) + v = a.process( + token_address=token_address, + token_symbol=token_symbol, + writer=self.__outputs_writer, + ) self.outputs.append(v) diff --git a/cic/data/config/config.ini b/cic/data/config/config.ini index 9a77060..4311461 100644 --- a/cic/data/config/config.ini +++ b/cic/data/config/config.ini @@ -1,5 +1,20 @@ [cic_core] -meta_writer = cic.output.KVWriter -attachment_writer = cic.output.KVWriter -proof_writer = cic.output.KVWriter -ext_writer = cic.output.KVWriter +meta_writer = cic.writers.KVWriter +attachment_writer = cic.writers.KVWriter +proof_writer = cic.writers.KVWriter +ext_writer = cic.writers.KVWriter + +[cic] +registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 + +[meta] +url = http://localhost:63380 +http_origin = + +[auth] +type = gnupg +db_path = /home/will/.local/share/cic/clicada +keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc +key = CCE2E1D2D0E36ADE0405E2D0995BB21816313BD5 +passphrase = merman + diff --git a/cic/data/token_template_v0.json b/cic/data/token_template_v0.json index 06afef1..628001c 100644 --- a/cic/data/token_template_v0.json +++ b/cic/data/token_template_v0.json @@ -4,5 +4,10 @@ "precision": 0, "code": null, "supply": 0, - "extra": {} + "extra": [ + { + "arg": "", + "arg_type": "" + } + ] } diff --git a/cic/ext/eth/__init__.py b/cic/ext/eth/__init__.py index a8ec650..edcd875 100644 --- a/cic/ext/eth/__init__.py +++ b/cic/ext/eth/__init__.py @@ -1,43 +1,41 @@ # standard imports -import logging import copy import json +import logging # external imports from chainlib.chain import ChainSpec -from chainlib.eth.tx import ( - TxFormat, - TxFactory, - Tx, - receipt, - ) +from chainlib.eth.address import is_address, to_checksum_address from chainlib.eth.connection import RPCConnection -from chainlib.eth.contract import ( - ABIContractEncoder, - ABIContractType - ) +from chainlib.eth.contract import ABIContractEncoder, ABIContractType from chainlib.eth.gas import OverrideGasOracle from chainlib.eth.nonce import RPCNonceOracle -from chainlib.eth.address import ( - is_address, - to_checksum_address, - ) -from hexathon import add_0x -from eth_token_index import TokenUniqueSymbolIndex +from chainlib.eth.tx import Tx, TxFactory, TxFormat, receipt from eth_address_declarator import Declarator from eth_address_declarator.declarator import AddressDeclarator +from eth_token_index import TokenUniqueSymbolIndex from giftable_erc20_token import GiftableToken +from hexathon import add_0x, strip_0x # local imports from cic.ext.eth.rpc import parse_adapter from cic.extension import Extension + logg = logging.getLogger(__name__) class CICEth(Extension): - - def __init__(self, chain_spec, resources, proof, signer=None, rpc=None, outputs_writer=None, fee_oracle=None): + def __init__( + self, + chain_spec, + resources, + proof, + signer=None, + rpc=None, + outputs_writer=None, + fee_oracle=None, + ): """Implementation for the eth extension. @@ -54,19 +52,25 @@ class CICEth(Extension): :param rpc: RPC adapter capable of submitting and querying the chain network node :type rpc: chainlib.connection.RPCConnection :param outputs_writer: Writer interface receiving the output of the processor - :type outputs_writer: cic.output.OutputWriter + :type outputs_writer: cic.writers.OutputWriter :param fee_oracle: Fee oracle required by signer :type fee_oracle: chainlib.fee.FeeOracle """ - super(CICEth, self).__init__(chain_spec, resources, proof, signer=signer, rpc=rpc, outputs_writer=outputs_writer) + super(CICEth, self).__init__( + chain_spec, + resources, + proof, + signer=signer, + rpc=rpc, + outputs_writer=outputs_writer, + ) self.fee_oracle = fee_oracle self.tx_format = TxFormat.RAW_ARGS - if self.rpc != None: + if self.rpc is not None: self.tx_format = TxFormat.JSONRPC - elif self.signer != None: + elif self.signer is not None: self.tx_format = TxFormat.RLP_SIGNED - def __detect_arg_type(self, v): typ = None try: @@ -74,59 +78,59 @@ class CICEth(Extension): typ = ABIContractType.UINT256 except TypeError: pass - - if typ == None: + + if typ is None: try: vv = strip_0x(v) if is_address(vv): - typ = ABIContractType.ADDRESS + typ = ABIContractType.ADDRESS else: - typ = ABIContractType.BYTES32 + typ = ABIContractType.BYTES32 except ValueError: pass - if typ == None: + if typ is None: try: - v.encode('utf-8') - typ = ABIContractType.STRING + v.encode("utf-8") + typ = ABIContractType.STRING except ValueError: pass - if typ == None: - raise ValueError('cannot automatically determine type for value {}'.format(v)) + if typ is None: + raise ValueError( + f"cannot automatically determine type for value {v}" + ) - logg.info('argument {} parsed as abi contract type {}'.format(typ.value)) + logg.info(f"argument {v} parsed as abi contract type {typ.value}") return typ - def __order_args(self): args = [ - self.token_details['name'], - self.token_details['symbol'], - self.token_details['precision'], - ] + self.token_details["name"], + self.token_details["symbol"], + self.token_details["precision"], + ] args_types = [ - ABIContractType.STRING.value, - ABIContractType.STRING.value, - ABIContractType.UINT256.value, - ] + ABIContractType.STRING.value, + ABIContractType.STRING.value, + ABIContractType.UINT256.value, + ] - for i, x in enumerate(self.token_details['extra']): + for i, x in enumerate(self.token_details["extra"]): args.append(x) typ = None - if self.token_details['extra_types'] != None: - typ = self.token_details['extra_types'][i] + if self.token_details["extra_types"] is not None: + typ = self.token_details["extra_types"][i] else: typ = self.__detect_arg_type(x) args_types.append(typ) - positions = self.token_details['positions'] - if positions == None: + positions = self.token_details["positions"] + if positions is None: positions = list(range(len(args))) - - return (args, args_types, positions) + return (args, args_types, positions) def add_outputs(self, k, v): """Adds given key/value pair to outputs array. @@ -136,10 +140,9 @@ class CICEth(Extension): :param v: Output value :param v: bytes or str """ - logg.debug('adding outputs {} {}'.format(k, v)) + logg.debug(f"adding outputs {k} {v}") self.outputs.append((k, v)) - def get_outputs(self): """Get wrapper for outputs captured from processing. @@ -148,14 +151,13 @@ class CICEth(Extension): """ return self.outputs - def process_token(self, writer=None): """Deploy token, and optionally mint token supply to token deployer account. :param writer: Writer interface receiving the output of the processor step - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter """ - if writer == None: + if writer is None: writer = self.outputs_writer (args, args_types, positions) = self.__order_args() @@ -163,143 +165,189 @@ class CICEth(Extension): enc = ABIContractEncoder() for i in positions: - getattr(enc, args_types[i])(args[i]) + getattr(enc, args_types[i])(args[i]) code = enc.get() - if self.token_code != None: + if self.token_code is not None: code = self.token_code + code - logg.debug('resource {}'.format(self.resources)) - signer_address = add_0x(to_checksum_address(self.resources['token']['key_account'])) + logg.debug(f"resource {self.resources}") + signer_address = add_0x( + to_checksum_address(self.resources["token"]["key_account"]) + ) nonce_oracle = None - if self.rpc != None: + if self.rpc is not None: nonce_oracle = RPCNonceOracle(signer_address, conn=self.rpc) - c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=self.fee_oracle) + c = TxFactory( + self.chain_spec, + signer=self.signer, + nonce_oracle=nonce_oracle, + gas_oracle=self.fee_oracle, + ) tx = c.template(signer_address, None, use_nonce=True) tx = c.set_code(tx, code) o = c.finalize(tx, self.tx_format) token_address_tx = None r = None - if self.rpc != None: + if self.rpc is not None: r = self.rpc.do(o[1]) token_address_tx = r o = self.rpc.wait(r) o = Tx.src_normalize(o) - self.token_address = o['contract_address'] - elif self.signer != None: + self.token_address = o["contract_address"] + elif self.signer is not None: r = o[1] token_address_tx = r - if r == None: + if r is None: r = code - writer.write('token', r.encode('utf-8')) - writer.write('token_address', self.token_address.encode('utf-8')) - self.add_outputs('token', r) + writer.write("token", r.encode("utf-8")) + writer.write("token_address", self.token_address.encode("utf-8")) + self.add_outputs("token", r) - if self.token_details['supply'] > 0: - c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=self.fee_oracle) - o = c.mint_to(self.token_address, self.resources['token']['key_account'], self.resources['token']['key_account'], self.token_details['supply']) + if int(self.token_details["supply"]) > 0: + c = GiftableToken( + self.chain_spec, + signer=self.signer, + nonce_oracle=nonce_oracle, + gas_oracle=self.fee_oracle, + ) + o = c.mint_to( + self.token_address, + self.resources["token"]["key_account"], + self.resources["token"]["key_account"], + self.token_details["supply"], + ) r = None - if self.rpc != None: + if self.rpc is not None: r = self.rpc.do(o[1]) self.rpc.wait(r) - writer.write('token_supply', r.encode('utf-8')) - elif self.signer != None: + writer.write("token_supply", r.encode("utf-8")) + elif self.signer is not None: r = o[1] - writer.write('token_supply', json.dumps(r).encode('utf-8')) + writer.write( + "token_supply", json.dumps(r, separators=(",", ":")).encode("utf-8") + ) else: r = o - writer.write('token_supply', r.encode('utf-8')) + writer.write("token_supply", r.encode("utf-8")) return token_address_tx - def process_token_index(self, writer=None): """Register deployed token with token index. :param writer: Writer interface receiving the output of the processor step - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter """ - if writer == None: + if writer is None: writer = self.outputs_writer - signer_address = add_0x(to_checksum_address(self.resources['token_index']['key_account'])) - contract_address = add_0x(to_checksum_address(self.resources['token_index']['reference'])) + signer_address = add_0x( + to_checksum_address(self.resources["token_index"]["key_account"]) + ) + contract_address = add_0x( + to_checksum_address(self.resources["token_index"]["reference"]) + ) - gas_oracle = OverrideGasOracle(limit=TokenUniqueSymbolIndex.gas(), conn=self.rpc) + gas_oracle = OverrideGasOracle( + limit=TokenUniqueSymbolIndex.gas(), conn=self.rpc + ) nonce_oracle = None - if self.rpc != None: + if self.rpc is not None: nonce_oracle = RPCNonceOracle(add_0x(signer_address), conn=self.rpc) - c = TokenUniqueSymbolIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) - - o = c.register(contract_address, signer_address, self.token_address, tx_format=self.tx_format) + c = TokenUniqueSymbolIndex( + self.chain_spec, + signer=self.signer, + nonce_oracle=nonce_oracle, + gas_oracle=gas_oracle, + ) + + o = c.register( + contract_address, + signer_address, + self.token_address, + tx_format=self.tx_format, + ) r = None - if self.rpc != None: + if self.rpc is not None: r = self.rpc.do(o[1]) self.rpc.wait(r) - elif self.signer != None: + elif self.signer is not None: r = o[1] else: r = o - writer.write('token_index', r.encode('utf-8')) - self.add_outputs('token_index', r) + writer.write("token_index", r.encode("utf-8")) + self.add_outputs("token_index", r) return r - def process_address_declarator(self, writer=None): """Register token proofs with address declarator. :param writer: Writer interface receiving the output of the processor step - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter """ - if writer == None: + if writer is None: writer = self.outputs_writer - signer_address = add_0x(to_checksum_address(self.resources['address_declarator']['key_account'])) - contract_address = add_0x(to_checksum_address(self.resources['address_declarator']['reference'])) + signer_address = add_0x( + to_checksum_address(self.resources["address_declarator"]["key_account"]) + ) + contract_address = add_0x( + to_checksum_address(self.resources["address_declarator"]["reference"]) + ) gas_oracle = OverrideGasOracle(limit=AddressDeclarator.gas(), conn=self.rpc) nonce_oracle = None - if self.rpc != None: + if self.rpc is not None: nonce_oracle = RPCNonceOracle(signer_address, conn=self.rpc) - c = Declarator(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) + c = Declarator( + self.chain_spec, + signer=self.signer, + nonce_oracle=nonce_oracle, + gas_oracle=gas_oracle, + ) results = [] - #(main_proof, all_proofs) = self.proof.get() + # (main_proof, all_proofs) = self.proof.get() - #for proof in all_proofs: - #logg.debug('proof {} '.format(proof)) + # for proof in all_proofs: + # logg.debug('proof {} '.format(proof)) (k, v) = self.proof.root() - fk = 'address_declarator_' + k - o = c.add_declaration(contract_address, signer_address, self.token_address, k, tx_format=self.tx_format) + fk = "address_declarator_" + k + o = c.add_declaration( + contract_address, + signer_address, + self.token_address, + k, + tx_format=self.tx_format, + ) r = None - if self.rpc != None: + if self.rpc is not None: r = self.rpc.do(o[1]) self.rpc.wait(r) - elif self.signer != None: + elif self.signer is not None: r = o[1] else: r = o self.add_outputs(fk, r) results.append(r) - v = r.encode('utf-8') - if writer != None: + v = r.encode("utf-8") + if writer is not None: writer.write(fk, v) return results - def prepare_extension(self): - """Sets token address for extension if defined in settings. - """ + """Sets token address for extension if defined in settings.""" super(CICEth, self).prepare_extension() - if self.token_address != None: + if self.token_address is not None: self.token_address = add_0x(to_checksum_address(self.token_address)) @@ -308,4 +356,11 @@ def new(chain_spec, resources, proof, signer_hint=None, rpc=None, outputs_writer See CICEth constructor for details. """ - return CICEth(chain_spec, resources, proof, signer=signer_hint, rpc=rpc, outputs_writer=outputs_writer) + return CICEth( + chain_spec, + resources, + proof, + signer=signer_hint, + rpc=rpc, + outputs_writer=outputs_writer, + ) diff --git a/cic/ext/eth/rpc.py b/cic/ext/eth/rpc.py index 8d53582..19b4ca5 100644 --- a/cic/ext/eth/rpc.py +++ b/cic/ext/eth/rpc.py @@ -20,12 +20,13 @@ class EthKeystoreDirectory(DictKeystore, KeystoreDirectory): TODO: Move to funga """ - pass + + def parse_adapter(config, signer_hint): """Determine and instantiate signer and rpc from configuration. - + If either could not be determined, None is returned. :param config: Configuration object implementing the get() method @@ -36,12 +37,12 @@ def parse_adapter(config, signer_hint): :return: RPC interface, signer interface """ keystore = None - if signer_hint == None: - logg.info('signer hint missing') + if signer_hint is None: + logg.info("signer hint missing") return None st = os.stat(signer_hint) if stat.S_ISDIR(st.st_mode): - logg.debug('signer hint is directory') + logg.debug("signer hint is directory") keystore = EthKeystoreDirectory() keystore.process_dir(signer_hint) diff --git a/cic/ext/eth/start.py b/cic/ext/eth/start.py index 1c327ea..1482c74 100644 --- a/cic/ext/eth/start.py +++ b/cic/ext/eth/start.py @@ -10,6 +10,7 @@ def extension_start(network, *args, **kwargs): :type network: cic.network.Network """ CICRegistry.address = kwargs['registry_address'] + key_account_address = kwargs['key_account_address'] or '' RPCConnection.register_location(kwargs['rpc_provider'], kwargs['chain_spec']) conn = RPCConnection.connect(kwargs['chain_spec']) @@ -17,10 +18,13 @@ def extension_start(network, *args, **kwargs): registry = CICRegistry(kwargs['chain_spec'], conn) address_declarator = registry.by_name('AddressDeclarator') - network.resource_set('eth', 'address_declarator', address_declarator) + network.resource_set('eth', 'address_declarator', address_declarator, key_account=key_account_address) token_index = registry.by_name('TokenRegistry') - network.resource_set('eth', 'token_index', token_index) + network.resource_set('eth', 'token_index', token_index, key_account=key_account_address) + + network.resource_set('eth', 'token', None, key_account=key_account_address) + network.set('eth', kwargs['chain_spec']) network.save() diff --git a/cic/extension.py b/cic/extension.py index 90cf83e..4abb1d7 100644 --- a/cic/extension.py +++ b/cic/extension.py @@ -5,7 +5,8 @@ import logging from hexathon import valid as valid_hex # local imports -from cic.output import StdoutWriter +from cic.writers import StdoutWriter +from cic.contract.components.token import Token logg = logging.getLogger(__name__) @@ -24,9 +25,18 @@ class Extension: :param rpc: RPC adapter capable of submitting and querying the chain network node :type rpc: chainlib.connection.RPCConnection :param writer: Writer interface receiving the output of the processor - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter """ - def __init__(self, chain_spec, resources, proof, signer=None, rpc=None, outputs_writer=StdoutWriter()): + + def __init__( + self, + chain_spec, + resources, + proof, + signer=None, + rpc=None, + outputs_writer=StdoutWriter(), + ): self.resources = resources self.proof = proof self.chain_spec = chain_spec @@ -38,9 +48,8 @@ class Extension: self.outputs = [] self.outputs_writer = outputs_writer - # TODO: apply / prepare token can be factored out - def apply_token(self, token): + def apply_token(self, token: Token): """Initialize extension with token data from settings. :param token: Token object @@ -48,10 +57,27 @@ class Extension: :rtype: dict :returns: Token data state of extension after load """ - return self.prepare_token(token.name, token.symbol, token.precision, token.code, token.supply) - + return self.prepare_token( + token.name, + token.symbol, + token.precision, + token.code, + token.supply, + token.extra_args, + token.extra_args_types, + ) - def prepare_token(self, name, symbol, precision, code, supply, extra=[], extra_types=[], positions=None): + def prepare_token( + self, + name, + symbol, + precision, + code, + supply, + extra=None, + extra_types=None, + positions=None, + ): """Initialize extension token data. :param name: Token name @@ -65,7 +91,7 @@ class Extension: :param supply: Token supply (in smallest precision units) :type supply: int :param extra: Extra parameters to pass to token application constructor - :type extra: list + :type extra: list :param extra_types: Type specifications for extra parameters :type extra_types: list :param positions: Sequence of parameter indices to pass to application constructor @@ -74,22 +100,20 @@ class Extension: :returns: Token data state of extension after load """ self.token_details = { - 'name': name, - 'symbol': symbol, - 'precision': precision, - 'code': code, - 'supply': supply, - 'extra': extra, - 'extra_types': extra_types, - 'positions': positions, - } + "name": name, + "symbol": symbol, + "precision": precision, + "code": code, + "supply": supply, + "extra": extra or [], + "extra_types": extra_types or [], + "positions": positions, + } + logg.debug(f"token details: {self.token_details}") return self.token_details - def prepare_extension(self): - """Prepare extension for publishing (noop) - """ - pass + """Prepare extension for publishing (noop)""" def parse_code_as_file(self, v): @@ -101,17 +125,14 @@ class Extension: :type v: str """ try: - f = open(v, 'r') + f = open(v, "r", encoding="utf-8") r = f.read() f.close() self.parse_code_as_hex(r) - except FileNotFoundError: - logg.debug('could not parse code as file: {}'.format(e)) - pass - except IsADirectoryError: - logg.debug('could not parse code as file: {}'.format(e)) - pass - + except FileNotFoundError as e: + logg.debug(f"could not parse code as file: {e}") + except IsADirectoryError as e: + logg.debug(f"could not parse code as file: {e}") def parse_code_as_hex(self, v): """Helper method to load application bytecode from hex data into extension token data state. @@ -121,12 +142,10 @@ class Extension: :param v: Bytecode as hex :type v: str """ - try: + try: self.token_code = valid_hex(v) except ValueError as e: - logg.debug('could not parse code as hex: {}'.format(e)) - pass - + logg.debug(f"could not parse code as hex: {e}") def load_code(self, hint=None): """Attempt to load token application bytecode using token settings. @@ -136,57 +155,57 @@ class Extension: :rtype: str (hex) :return: Bytecode loaded into extension token data state """ - code = self.token_details['code'] - if hint == 'hex': + code = self.token_details["code"] + if hint == "hex": self.token_code = valid_hex(code) - + for m in [ - self.parse_code_as_hex, - self.parse_code_as_file, - ]: + self.parse_code_as_hex, + self.parse_code_as_file, + ]: m(code) - if self.token_code != None: + if self.token_code is not None: break - - if self.token_code == None: - raise RuntimeError('could not successfully parse token code') + + if self.token_code is None: + raise RuntimeError("could not successfully parse token code") return self.token_code - def process(self, writer=None): """Adapter used by Processor to process the extensions implementing the Extension base class. - Requires either token address or a valid token code reference to have been included in settings. If token address is not set, the token application code will be deployed. + Requires either token address or a valid token code reference to have been included in settings. + If token address is not set, the token application code will be deployed. :param writer: Writer to use for publishing. - :type writer: cic.output.OutputWriter + :type writer: cic.writers.OutputWriter :rtype: tuple :return: Token address, token symbol """ - if writer == None: + if writer is None: writer = self.outputs_writer - tasks = [] - self.token_address = self.resources['token']['reference'] - + tasks = [] + self.token_address = self.resources["token"]["reference"] + # TODO: get token details when token address is not none - if self.token_address == None: - if self.token_details['code'] == None: - raise RuntimeError('neither token address nor token code has been set') + if self.token_address is None: + if self.token_details["code"] is None: + raise RuntimeError("neither token address nor token code has been set") self.load_code() - tasks.append('token') + tasks.append("token") for k in self.resources.keys(): - if k == 'token': + if k == "token": continue - if self.resources[k]['reference'] != None: + if self.resources[k]["reference"] is not None: tasks.append(k) - + self.prepare_extension() for task in tasks: - logg.debug('extension adapter process {}'.format(task)) - r = getattr(self, 'process_' + task)(writer=writer) + logg.debug(f"extension adapter process {task}") + _r = getattr(self, "process_" + task)(writer=writer) - return (self.token_address, self.token_details.get('symbol')) + return (self.token_address, self.token_details.get("symbol")) diff --git a/cic/hash.py b/cic/hash.py deleted file mode 100644 index 34eafd3..0000000 --- a/cic/hash.py +++ /dev/null @@ -1,10 +0,0 @@ -class Hasher: - - def __basehasher(self, v): - h = hashlib.sha256() - h.update(v) - return h.digest() - - - def hash(self, v): - return self.__basehasher(v) diff --git a/cic/keystore.py b/cic/keystore.py index 24cce56..8132e4f 100644 --- a/cic/keystore.py +++ b/cic/keystore.py @@ -23,9 +23,9 @@ class KeystoreDirectory(Keystore): except IsADirectoryError: pass except KeyfileError as e: - logg.warning('file {} could not be parsed as keyfile: {}'.format(fp, e)) + logg.warning(f'file {fp} could not be parsed as keyfile: {e}') except DecryptError as e: - if password_retriever == None: + if password_retriever is None: raise e password = password_retriever() self.import_keystore_file(fp, password=password) diff --git a/cic/meta.py b/cic/meta.py deleted file mode 100644 index a4bcce5..0000000 --- a/cic/meta.py +++ /dev/null @@ -1,141 +0,0 @@ -# standard imports -import os -import json -import logging -import base64 - -# external imports -from cic_types import MetadataPointer -from cic_types.processor import generate_metadata_pointer -from cic_types.ext.metadata import MetadataRequestsHandler -from hexathon import strip_0x - -# local imports -from .base import ( - Data, - data_dir, - ) -from cic.output import OutputWriter -logg = logging.getLogger(__name__) - - -class Meta(Data): - """Serialize and publish metadata for token. - - The token metadata is any mutable data that is not part of the initial token proof, but published simultaneously as the token nonetheless. - - :param path: Path to settings directory - :type path: str - :param writer: Writer interface receiving the output of the processor - :type writer: cic.output.OutputWriter - """ - def __init__(self, path='.', writer=None): - super(Meta, self).__init__() - self.name = None - self.contact = {} - self.path = path - self.writer = writer - self.meta_path = os.path.join(self.path, 'meta.json') - - - def load(self): - """Load metadata from settings. - """ - super(Meta, self).load() - - f = open(self.meta_path, 'r') - o = json.load(f) - f.close() - - self.name = o['name'] - self.contact = o['contact'] - - self.inited = True - - - def start(self): - """Initialize metadata settings from template. - """ - super(Meta, self).start() - - meta_template_file_path = os.path.join(data_dir, 'meta_template_v{}.json'.format(self.version())) - - f = open(meta_template_file_path) - o = json.load(f) - f.close() - - f = open(self.meta_path, 'w') - json.dump(o, f, sort_keys=True, indent="\t") - f.close() - - - def reference(self, token_address): - """Calculate the mutable reference for the token metadata. - """ - token_address_bytes = bytes.fromhex(strip_0x(token_address)) - return generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_META) - - - def asdict(self): - """Output proof state to dict. - """ - return { - 'name': self.name, - 'contact': self.contact, - } - - - def process(self, token_address=None, token_symbol=None, writer=None): - """Serialize and publish metadata. - - See cic.processor.Processor.process - """ - if writer == None: - writer = self.writer - - v = json.dumps(self.asdict()) - - token_address_bytes = bytes.fromhex(strip_0x(token_address)) - k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_META) - writer.write(k, v.encode('utf-8')) - - token_symbol_bytes = token_symbol.encode('utf-8') - k = generate_metadata_pointer(token_symbol_bytes, MetadataPointer.TOKEN_META_SYMBOL) - writer.write(k, v.encode('utf-8')) - - return (k, v) - - - def __str__(self): - s = "contact.name = {}\n".format(self.name) - - for k in self.contact.keys(): - if self.contact[k] == '': - continue - s += "contact.{} = {}\n".format(k.lower(), self.contact[k]) - - return s - - -class MetadataWriter(OutputWriter): - """Custom writer for publishing data under immutable content-addressed pointers in the cic-meta storage backend. - - Data that is not utf-8 will be converted to base64 before publishing. - - Implements cic.output.OutputWriter - """ - - def write(self, k, v): - rq = MetadataRequestsHandler(MetadataPointer.NONE, bytes.fromhex(k)) - try: - v = v.decode('utf-8') - v = json.loads(v) - logg.debug('metadatawriter bindecode {} {}'.format(k, v)) - except UnicodeDecodeError: - v = base64.b64encode(v).decode('utf-8') - v = json.loads(json.dumps(v)) - logg.debug('metadatawriter b64encode {} {}'.format(k, v)) - r = rq.create(v) - logg.info('metadata submitted at {}'.format(k)) - return r - diff --git a/cic/proof.py b/cic/proof.py deleted file mode 100644 index bfa16d0..0000000 --- a/cic/proof.py +++ /dev/null @@ -1,181 +0,0 @@ -# standard imports -import os -import json -import logging -import tempfile -import cbor2 - -# external imports -from hexathon import strip_0x -from cic_types import MetadataPointer -from cic_types.processor import generate_metadata_pointer -from cic_types.ext.metadata import MetadataRequestsHandler - -# local imports -from .base import * -from cic.output import OutputWriter - -logg = logging.getLogger(__name__) - - -class Proof(Data): - """Proof handles the immutable token proof data mapped to the initial token deployment. - - It processes inputs from the proof.json file in the session directory. - - Optionally, attachment objects can be added to the proof. If added, the resulting proof digest will consists of the attachment digests added to the root digest. These are then are deterministically ordered, regardless of which order attachments were given to the constructor. - - :param path: Path to settings directory - :type path: str - :param attachments: List of attachment objects to include in the proof - :type attachments: cic.attachment.Attachment - :param writer: Writer interface receiving the output of the processor - :type writer: cic.output.OutputWriter - """ - - def __init__(self, path='.', attachments=None, writer=None): - super(Proof, self).__init__() - self.proofs = [] - self.namespace = 'ge' - self.description = None - self.issuer = None - self.path = path - self.writer = writer - self.extra_attachments = attachments - self.attachments = {} - self.proof_path = os.path.join(self.path, 'proof.json') - self.temp_proof_path = tempfile.mkstemp()[1] - - - def load(self): - """Load proof data from settings. - """ - super(Proof, self).load() - - f = open(self.proof_path, 'r') - o = json.load(f) - f.close() - - self.set_version(o['version']) - self.description = o['description'] - self.namespace = o['namespace'] - self.issuer = o['issuer'] - self.proofs = o['proofs'] - - if self.extra_attachments != None: - a = self.extra_attachments.asdict() - for k in a.keys(): - self.attachments[k] = a[k] - - hshs = self.__get_ordered_hashes() - self.proofs = list(map(strip_0x, hshs)) - - self.inited = True - - - def start(self): - """Initialize proof settings from template. - """ - super(Proof, self).start() - - proof_template_file_path = os.path.join(data_dir, 'proof_template_v{}.json'.format(self.version())) - - f = open(proof_template_file_path) - o = json.load(f) - f.close() - - f = open(self.proof_path, 'w') - json.dump(o, f, sort_keys=True, indent="\t") - f.close() - - - def asdict(self): - """Output proof state to dict. - """ - return { - 'version': self.version(), - 'namespace': self.namespace, - 'description': self.description, - 'issuer': self.issuer, - 'proofs': self.proofs, - } - - - # TODO: the efficiency of this method could probably be improved. - def __get_ordered_hashes(self): - ks = list(self.attachments.keys()) - ks.sort() - - return ks - - -# def get(self): -# hsh = self.hash(b).hex() -# self.attachments[hsh] = self.temp_proof_path -# logg.debug('cbor of {} is {} hashes to {}'.format(v, b.hex(), hsh)) - - - def root(self): - """Calculate the root digest from the serialized proof object. - """ - v = self.asdict() - #b = cbor2.dumps(v) - b = json.dumps(v) - - f = open(self.temp_proof_path, 'w') - f.write(b) - f.close() - - b = b.encode('utf-8') - k = self.hash(b) - - return (k.hex(), b) - - - def process(self, token_address=None, token_symbol=None, writer=None): - """Serialize and publish proof. - - See cic.processor.Processor.process - """ - if writer == None: - writer = self.writer - - (k, v) = self.root() - writer.write(k, v) - root_key = k - - token_symbol_bytes = token_symbol.encode('utf-8') - k = generate_metadata_pointer(token_symbol_bytes, MetadataPointer.TOKEN_PROOF_SYMBOL) - writer.write(k, v) - - token_address_bytes = bytes.fromhex(strip_0x(token_address)) - k = generate_metadata_pointer(token_address_bytes, MetadataPointer.TOKEN_PROOF) - writer.write(k, v) - -# (hsh, hshs) = self.get() - #hshs = list(map(strip_0x, hshs)) -# hshs_bin = list(map(bytes.fromhex, hshs)) -# hshs_cat = b''.join(hshs_bin) - -# f = open(self.temp_proof_path, 'rb') -# v = f.read() -# f.close() -# writer.write(hsh, v) - -# r = self.hash(hshs_cat) -# r_hex = r.hex() - - #logg.debug('generated proof {} for hashes {}'.format(r_hex, hshs)) - - #writer.write(r_hex, hshs_cat) - - o = self.asdict() - f = open(self.proof_path, 'w') - json.dump(o, f, sort_keys=True, indent="\t") - f.close() - - return root_key - - - def __str__(self): - return "description = {}\n".format(self.description) diff --git a/cic/runnable/cic_cmd.py b/cic/runnable/cic_cmd.py index 3804dbe..2ee4b10 100644 --- a/cic/runnable/cic_cmd.py +++ b/cic/runnable/cic_cmd.py @@ -1,16 +1,18 @@ # standard imports import os import logging -import argparse import sys import importlib # external imports import chainlib.cli + +# local imports import cic.cmd.init as cmd_init import cic.cmd.show as cmd_show import cic.cmd.ext as cmd_ext import cic.cmd.export as cmd_export +import cic.cmd.wizard as cmd_wizard logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() @@ -21,41 +23,53 @@ base_config_dir = os.path.join(data_dir, 'config') schema_dir = os.path.join(script_dir, '..', 'schema') arg_flags = chainlib.cli.argflag_std_read | chainlib.cli.Flag.SEQ -argparser = chainlib.cli.ArgumentParser(env=os.environ, arg_flags=arg_flags, description='CIC cli tool for generating and publishing tokens') +argparser = chainlib.cli.ArgumentParser( + env=os.environ, + arg_flags=arg_flags, + description='CIC cli tool for generating and publishing tokens' +) sub = argparser.add_subparsers() sub.dest = 'command' + sub_init = sub.add_parser('init', help='initialize new cic data directory') cmd_init.process_args(sub_init) + sub_show = sub.add_parser('show', help='display summary of current state of cic data directory') cmd_show.process_args(sub_show) + sub_export = sub.add_parser('export', help='export cic data directory state to a specified target') cmd_export.process_args(sub_export) + sub_ext = sub.add_parser('ext', help='extension helpers') cmd_ext.process_args(sub_ext) +sub_wizard = sub.add_parser('wizard', help='An interactive wizard for creating and publishing contracts') +cmd_wizard.process_args(sub_wizard) args = argparser.parse_args(sys.argv[1:]) -if args.command == None: +if args.command is None: logg.critical('Subcommand missing') sys.stderr.write("\033[;91m" + 'subcommand missing' + "\033[;39m\n") + argparser.print_help(sys.stderr) sys.exit(1) -modname = 'cic.cmd.{}'.format(args.command) -logg.debug('using module {}'.format(modname)) +modname = f'cic.cmd.{args.command}' +logg.debug(f'using module {modname}') cmd_mod = importlib.import_module(modname) extra_args = { 'p': 'RPC_PROVIDER', - } +} + config = chainlib.cli.Config.from_args(args, arg_flags=arg_flags, base_config_dir=base_config_dir, extra_args=extra_args) def main(): try: cmd_mod.execute(config, args) except Exception as e: - logg.exception(e) #'{}'.format(e)) + logg.exception(e) sys.stderr.write("\033[;91m" + str(e) + "\033[;39m\n") sys.exit(1) diff --git a/cic/token.py b/cic/token.py deleted file mode 100644 index 104be8b..0000000 --- a/cic/token.py +++ /dev/null @@ -1,88 +0,0 @@ -# standard imports -import os -import json - -# local imports -from .base import ( - Data, - data_dir, - ) - - -class Token(Data): - """Encapsulates the token data used by the extension to deploy and/or register token and token related applications on chain. - - Token details (name, symbol etc) will be used to initialize the token settings when start is called. If load is called instead, any token detail parameters passed to the constructor will be overwritten by data stored in the settings. - - :param path: Settings directory path - :type path: str - :param name: Token name - :type name: str - :param symbol: Token symbol - :type symbol: str - :param precision: Token value precision (number of decimals) - :type precision: int - :param supply: Token supply (in smallest precision units) - :type supply: int - :param code: Bytecode for token chain application - :type code: str (hex) - """ - def __init__(self, path='.', name=None, symbol=None, precision=1, supply=0, code=None): - super(Token, self).__init__() - self.name = name - self.symbol = symbol - self.supply = supply - self.precision = precision - self.code = code - self.extra_args = None - self.path = path - self.token_path = os.path.join(self.path, 'token.json') - - - def load(self): - """Load token data from settings. - """ - super(Token, self).load() - - f = open(self.token_path, 'r') - o = json.load(f) - f.close() - - self.name = o['name'] - self.symbol = o['symbol'] - self.precision = o['precision'] - self.code = o['code'] - self.supply = o['supply'] - self.extra_args = o['extra'] - - self.inited = True - - - def start(self): - """Initialize token settings from arguments passed to the constructor and/or template. - """ - super(Token, self).load() - - token_template_file_path = os.path.join(data_dir, 'token_template_v{}.json'.format(self.version())) - - f = open(token_template_file_path) - o = json.load(f) - f.close() - - o['name'] = self.name - o['symbol'] = self.symbol - o['precision'] = self.precision - o['code'] = self.code - o['supply'] = self.supply - - f = open(self.token_path, 'w') - json.dump(o, f, sort_keys=True, indent="\t") - f.close() - - - def __str__(self): - s = """name = {} -symbol = {} -precision = {} -""".format(self.name, self.symbol, self.precision) - return s diff --git a/cic/utils.py b/cic/utils.py new file mode 100644 index 0000000..9f934fa --- /dev/null +++ b/cic/utils.py @@ -0,0 +1,24 @@ +def object_to_str(obj, keys): + """Return a string representation of an object.""" + s = "" + for key in keys: + value = eval("obj." + key) + key = key.replace("()", "") + if type(value) == str: + s += f"{key} = {value}\n" + elif type(value) == list: + for idx, vv in enumerate(value): + if not vv: + s += f"{key}[{idx}] = \n" + continue + s += f"{key}[{idx}] = {vv}\n" + elif type(value) == dict: + for vv_key in value.keys(): + vv_value = value[vv_key] + if not vv_value: + s += f"{key}.{vv_key} = \n" + continue + s += f"{key}.{vv_key} = {vv_value}\n" + else: + s += f"{key} = {str(value)}\n" + return s diff --git a/cic/output.py b/cic/writers.py similarity index 87% rename from cic/output.py rename to cic/writers.py index 5d514d0..a84a9a7 100644 --- a/cic/output.py +++ b/cic/writers.py @@ -51,7 +51,7 @@ class HTTPWriter(OutputWriter): path = self.path if k != None: path = os.path.join(path, k) - logg.debug('http writer post {}'.format(path)) + logg.debug(f'http writer post {path} \n key: {k}, value: {v}') rq = urllib.request.Request(path, method='POST', data=v) r = urllib.request.urlopen(rq) logg.info('http writer submitted at {}'.format(r.read())) @@ -64,14 +64,14 @@ class KeyedWriter(OutputWriter): self.writer_immutable = writer_immutable - def write(self, k, v): - logg.debug('writing keywriter {} {}'.format(k, v)) - if isinstance(v, str): - v = v.encode('utf-8') + def write(self, key, value): + logg.debug(f'writing keywriter key: {key} value: {value}') + if isinstance(value, str): + value = value.encode('utf-8') if self.writer_keyed != None: - self.writer_keyed.write(k, v) + self.writer_keyed.write(key, value) if self.writer_immutable != None: - self.writer_immutable.write(None, v) + self.writer_immutable.write(None, value) class KeyedWriterFactory: diff --git a/config/dev-docker/config.ini b/config/dev-docker/config.ini new file mode 100644 index 0000000..bf92761 --- /dev/null +++ b/config/dev-docker/config.ini @@ -0,0 +1,29 @@ +[cic_core] +meta_writer = cic.writers.KVWriter +attachment_writer = cic.writers.KVWriter +proof_writer = cic.writers.KVWriter +ext_writer = cic.writers.KVWriter + +[cic] +registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 + +[meta] +url = http://localhost:63380 +http_origin = + +[rpc] +provider = http://localhost:63545 + +[auth] +type = gnupg +db_path = ~/.local/share/cic/clicada +key = eb3907ecad74a0013c259d5874ae7f22dcbcc95c +keyfile_path = ~/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc +passphrase = merman + +[wallet] +key_file = ~/grassroots/cic-internal-integration/apps/contract-migration/keystore/UTC +passphrase = + +[chain] +spec = evm:byzantium:8996:bloxberg \ No newline at end of file diff --git a/config/prod/config.ini b/config/prod/config.ini new file mode 100644 index 0000000..45a2e48 --- /dev/null +++ b/config/prod/config.ini @@ -0,0 +1,29 @@ +[cic_core] +meta_writer = cic.writers.KVWriter +attachment_writer = cic.writers.KVWriter +proof_writer = cic.writers.KVWriter +ext_writer = cic.writers.KVWriter + +[cic] +registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 + +[meta] +url = https://meta.grassecon.net +http_origin = + +[rpc] +provider = https://rpc.grassecon.net + +[auth] +type = gnupg +db_path = ~/.local/share/cic/clicada +key = CCE2E1D2D0E36ADE0405E2D0995BB21816313BD5 +keyfile_path = ~/.config/cic/staff-client/user.asc +passphrase = queenmarlena + +[wallet] +key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore +passphrase = + +[chain] +spec = evm:byzantium:5050:bloxberg \ No newline at end of file diff --git a/eth_requirements.txt b/eth_requirements.txt deleted file mode 100644 index 5e5c822..0000000 --- a/eth_requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -chainlib-eth~=0.0.13 -funga-eth~=0.5.1 -eth-token-index~=0.2.4 -eth-address-index~=0.2.4 -okota~=0.2.5a1 -cic_eth_registry~=0.6.2 -cic_contracts~=0.0.5 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..eca897d --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2429 @@ +[[package]] +name = "asn1crypto" +version = "1.4.0" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "astroid" +version = "2.9.3" +description = "An abstract syntax tree for Python with inference support." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} +wrapt = ">=1.11,<1.14" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "black" +version = "22.1.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = ">=1.1.0" +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "blake2b-py" +version = "0.1.4" +description = "Blake2b hashing in Rust with Python bindings." +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "bleach" +version = "4.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "cached-property" +version = "1.5.2" +description = "A decorator for caching properties in classes." +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "cbor2" +version = "5.4.1" +description = "Pure Python CBOR (de)serializer with extensive tag support" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["pytest", "pytest-cov"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "cffi" +version = "1.15.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "chainlib" +version = "0.0.21" +description = "Generic blockchain access library and tooling" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +confini = ">=0.5.3,<0.6.0" +funga = ">=0.5.1,<0.6.0" +hexathon = ">=0.1.3,<0.2.0" +pysha3 = "1.0.2" + +[package.extras] +xdg = ["pyxdg (>=0.27,<1.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "chainlib-eth" +version = "0.0.26" +description = "Ethereum implementation of the chainlib interface" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +chainlib = ">=0.0.19,<0.1.0" +confini = ">=0.5.3,<0.6.0" +funga-eth = ">=0.5.3,<0.6.0" +hexathon = ">=0.1.5,<0.2.0" +potaahto = ">=0.1.0,<0.2.0" +pysha3 = "1.0.2" +websocket-client = "0.57.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "cic-contracts" +version = "0.0.5" +description = "CIC network smart contract interfaces" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.12,<0.1.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "cic-eth-registry" +version = "0.6.6" +description = "Contract registry" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.15,<0.1.0" +confini = ">=0.5.3,<0.6.0" +eth-accounts-index = ">=0.1.2,<0.2.0" +eth-contract-registry = ">=0.7.2,<0.8.0" +eth-erc20 = ">=0.1.5,<0.2.0" +eth-token-index = ">=0.2.4,<0.3.0" +funga-eth = ">=0.5.1,<0.6.0" +okota = ">=0.2.5,<0.3.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "cic-types" +version = "0.2.1a9" +description = "Defines descriptors for data objects specific to the CIC-Network." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +chainlib-eth = ">=0.0.14,<0.1.0" +jsonschema = "3.2.0" +phonenumbers = "8.12.12" +python-gnupg = "0.4.7" +requests = "2.26.0" +semver = "2.13.0" +vobject = "0.9.6.1" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "click" +version = "8.0.4" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "click-log" +version = "0.3.2" +description = "Logging integration for Click" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +click = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "coincurve" +version = "15.0.0" +description = "Cross-platform Python CFFI bindings for libsecp256k1" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +asn1crypto = "*" +cffi = ">=1.3.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "confini" +version = "0.5.5" +description = "Parse, verify and merge all ini files in a single directory" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +python-gnupg = ">=0.4.6,<0.5.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "coverage" +version = "6.3.2" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +toml = ["tomli"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "cryptography" +version = "3.2.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" + +[package.dependencies] +cffi = ">=1.8,<1.11.3 || >1.11.3" +six = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "cytoolz" +version = "0.11.2" +description = "Cython implementation of Toolz: High performance functional utilities" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "docutils" +version = "0.18.1" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "dotty-dict" +version = "1.3.0" +description = "Dictionary wrapper for quick access to deeply nested keys." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +setuptools_scm = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "eth-abi" +version = "2.1.1" +description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" +category = "dev" +optional = false +python-versions = ">=3.6, <4" + +[package.dependencies] +eth-typing = ">=2.0.0,<3.0.0" +eth-utils = ">=1.2.0,<2.0.0" +parsimonious = ">=0.8.0,<0.9.0" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel", "twine", "ipython", "pytest (==4.4.1)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (==1.22.3)", "tox (>=2.9.1,<3)", "eth-hash", "hypothesis (>=3.6.1,<4)", "flake8 (==3.4.1)", "isort (>=4.2.15,<5)", "mypy (==0.701)", "pydocstyle (>=3.0.0,<4)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)", "towncrier (>=19.2.0,<20)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)", "towncrier (>=19.2.0,<20)"] +lint = ["flake8 (==3.4.1)", "isort (>=4.2.15,<5)", "mypy (==0.701)", "pydocstyle (>=3.0.0,<4)"] +test = ["pytest (==4.4.1)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (==1.22.3)", "tox (>=2.9.1,<3)", "eth-hash", "hypothesis (>=3.6.1,<4)"] +tools = ["hypothesis (>=3.6.1,<4)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-accounts-index" +version = "0.1.2" +description = "Accounts index evm contract tooling with permissioned writes" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.10,<0.1.0" +confini = ">=0.5.1,<0.6.0" + +[package.extras] +testing = ["pytest (==6.0.1)", "eth-tester (==0.5.0b2)", "py-evm (==0.3.0a20)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-address-index" +version = "0.2.5" +description = "Signed metadata declarations for ethereum addresses" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.22,<0.1.0" +confini = ">=0.5.2,<0.6.0" + +[package.extras] +dev = ["eth-tester (==0.5.0b3)", "py-evm (==0.3.0a20)", "eth-accounts-index (>=0.1.3,<0.2.0)", "eth_erc20 (>=0.1.5,<0.2.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-bloom" +version = "1.0.4" +description = "Python implementation of the Ethereum Trie structure" +category = "dev" +optional = false +python-versions = ">=3.6, <4" + +[package.dependencies] +eth-hash = {version = ">=0.3.1,<0.4.0", extras = ["pycryptodome"]} + +[package.extras] +deploy = ["bumpversion (>=0.5.3,<1.0.0)", "wheel (>=0.30.0,<1.0.0)"] +dev = ["bumpversion (>=0.5.3,<1.0.0)", "wheel (>=0.30.0,<1.0.0)", "twine", "pytest (==3.0.7)", "hypothesis (==3.7.0)", "tox (==2.6.0)", "flake8 (>=3.5.0,<4.0.0)", "mypy (<0.600)"] +lint = ["flake8 (>=3.5.0,<4.0.0)", "mypy (<0.600)"] +test = ["pytest (==3.0.7)", "hypothesis (==3.7.0)", "tox (==2.6.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-contract-registry" +version = "0.7.2" +description = "Ethereum Smart Contract key-value registry" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.12,<0.1.0" +confini = ">=0.5.2,<0.6.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-erc20" +version = "0.1.8" +description = "ERC20 interface and simple contract with deployment script that lets any address mint and gift itself tokens." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.14,<0.1.0" +confini = ">=0.5.2,<0.6.0" +potaahto = ">=0.1.1,<0.2.0" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "eth-hash" +version = "0.3.2" +description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" +category = "main" +optional = false +python-versions = ">=3.5, <4" + +[package.dependencies] +pycryptodome = {version = ">=3.6.6,<4", optional = true, markers = "extra == \"pycryptodome\""} + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel", "twine", "ipython", "pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)", "flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.770)", "pydocstyle (>=5.0.0,<6)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<1)", "towncrier (>=19.2.0,<20)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<1)", "towncrier (>=19.2.0,<20)"] +lint = ["flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.770)", "pydocstyle (>=5.0.0,<6)"] +pycryptodome = ["pycryptodome (>=3.6.6,<4)"] +pysha3 = ["pysha3 (>=1.0.0,<2.0.0)"] +test = ["pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-keys" +version = "0.3.4" +description = "Common API for Ethereum key operations." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +eth-typing = ">=2.2.1,<3.0.0" +eth-utils = ">=1.8.2,<2.0.0" + +[package.extras] +coincurve = ["coincurve (>=7.0.0,<13.0.0)"] +dev = ["tox (==3.20.0)", "bumpversion (==0.5.3)", "twine", "eth-utils (>=1.8.2,<2.0.0)", "eth-typing (>=2.2.1,<3.0.0)", "flake8 (==3.0.4)", "mypy (==0.782)", "asn1tools (>=0.146.2,<0.147)", "factory-boy (>=3.0.1,<3.1)", "pyasn1 (>=0.4.5,<0.5)", "pytest (==5.4.1)", "hypothesis (>=5.10.3,<6.0.0)", "eth-hash", "eth-hash"] +eth-keys = ["eth-utils (>=1.8.2,<2.0.0)", "eth-typing (>=2.2.1,<3.0.0)"] +lint = ["flake8 (==3.0.4)", "mypy (==0.782)"] +test = ["asn1tools (>=0.146.2,<0.147)", "factory-boy (>=3.0.1,<3.1)", "pyasn1 (>=0.4.5,<0.5)", "pytest (==5.4.1)", "hypothesis (>=5.10.3,<6.0.0)", "eth-hash", "eth-hash"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "eth-tester" +version = "0.5.0b3" +description = "Tools for testing Ethereum applications." +category = "dev" +optional = false +python-versions = ">=3.6.8,<4" + +[package.dependencies] +eth-abi = ">=2.0.0b4,<3.0.0" +eth-keys = ">=0.2.1,<0.4.0" +eth-utils = ">=1.4.1,<2.0.0" +rlp = ">=1.1.0,<3" +semantic-version = ">=2.6.0,<3.0.0" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1.0.0)", "eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "flake8 (>=3.5.0,<4.0.0)", "py-evm (==0.3.0a20)", "pytest-xdist (>=1.22.2,<2)", "pytest (>=4.4.0,<5.0.0)", "tox (>=2.9.1,<3.0.0)", "wheel (>=0.30.0,<1.0.0)", "eth-hash[pysha3] (>=0.1.4,<1.0.0)", "eth-hash[pycryptodome] (>=0.1.4,<1.0.0)"] +lint = ["flake8 (>=3.5.0,<4.0.0)"] +py-evm = ["py-evm (==0.3.0a20)", "eth-hash[pysha3] (>=0.1.4,<1.0.0)", "eth-hash[pycryptodome] (>=0.1.4,<1.0.0)"] +pyevm = ["py-evm (==0.3.0a20)", "eth-hash[pysha3] (>=0.1.4,<1.0.0)", "eth-hash[pycryptodome] (>=0.1.4,<1.0.0)"] +test = ["eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "pytest-xdist (>=1.22.2,<2)", "pytest (>=4.4.0,<5.0.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-token-index" +version = "0.2.4.linux-x86_64" +description = "Token symbol to address unique index" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.10,<0.1.0" +confini = ">=0.5.1,<0.6.0" +eth_erc20 = ">=0.1.2,<0.2.0" + +[package.extras] +testing = ["eth-tester (==0.5.0b2)", "py-evm (==0.3.0a20)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "eth-typing" +version = "2.3.0" +description = "eth-typing: Common type annotations for ethereum python packages" +category = "main" +optional = false +python-versions = ">=3.5, <4" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel", "twine", "ipython", "pytest (>=4.4,<4.5)", "pytest-xdist", "tox (>=2.9.1,<3)", "flake8 (==3.8.3)", "isort (>=4.2.15,<5)", "mypy (==0.782)", "pydocstyle (>=3.0.0,<4)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"] +lint = ["flake8 (==3.8.3)", "isort (>=4.2.15,<5)", "mypy (==0.782)", "pydocstyle (>=3.0.0,<4)"] +test = ["pytest (>=4.4,<4.5)", "pytest-xdist", "tox (>=2.9.1,<3)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "eth-utils" +version = "1.10.0" +description = "eth-utils: Common utility functions for python code that interacts with Ethereum" +category = "main" +optional = false +python-versions = ">=3.5,!=3.5.2,<4" + +[package.dependencies] +cytoolz = {version = ">=0.10.1,<1.0.0", markers = "implementation_name == \"cpython\""} +eth-hash = ">=0.3.1,<0.4.0" +eth-typing = ">=2.2.1,<3.0.0" +toolz = {version = ">0.8.2,<1", markers = "implementation_name == \"pypy\""} + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel (>=0.30.0,<1.0.0)", "twine (>=1.13,<2)", "ipython", "hypothesis (>=4.43.0,<5.0.0)", "pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)", "black (>=18.6b4,<19)", "flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.720)", "pydocstyle (>=5.0.0,<6)", "pytest (>=3.4.1,<4.0.0)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<2)", "towncrier (>=19.2.0,<20)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<2)", "towncrier (>=19.2.0,<20)"] +lint = ["black (>=18.6b4,<19)", "flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.720)", "pydocstyle (>=5.0.0,<6)", "pytest (>=3.4.1,<4.0.0)"] +test = ["hypothesis (>=4.43.0,<5.0.0)", "pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "funga" +version = "0.5.1" +description = "A signer and keystore daemon and library for cryptocurrency software development" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "funga-eth" +version = "0.5.5" +description = "Ethereum implementation of the funga keystore and signer" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +coincurve = "15.0.0" +confini = ">=0.5.1,<0.6.0" +cryptography = "3.2.1" +funga = "0.5.1" +hexathon = ">=0.1.0,<0.2.0" +json-rpc = "1.13.0" +pycryptodome = "3.10.1" +pysha3 = "1.0.2" +rlp = "2.0.1" + +[package.extras] +sql = ["psycopg2 (==2.8.6)", "sqlalchemy (==1.3.20)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "gitdb" +version = "4.0.9" +description = "Git Object Database" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "gitpython" +version = "3.1.27" +description = "GitPython is a python library used to interact with Git repositories" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "hexathon" +version = "0.1.5" +description = "Common and uncommon hex string operations" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "hexbytes" +version = "0.2.2" +description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" +category = "dev" +optional = false +python-versions = ">=3.6, <4" + +[package.extras] +dev = ["Sphinx (>=1.6.5,<2)", "bumpversion (>=0.5.3,<1)", "eth-utils (>=1.0.1,<2)", "flake8 (==3.7.9)", "hypothesis (>=3.44.24,<4)", "ipython", "isort (>=4.2.15,<5)", "mypy (==0.770)", "pydocstyle (>=5.0.0,<6)", "pytest-watch (>=4.1.0,<5)", "pytest-xdist", "pytest (==5.4.1)", "sphinx-rtd-theme (>=0.1.9,<1)", "towncrier (>=19.2.0,<20)", "tox (==3.14.6)", "twine", "wheel"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<1)", "towncrier (>=19.2.0,<20)"] +lint = ["flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.770)", "pydocstyle (>=5.0.0,<6)"] +test = ["eth-utils (>=1.0.1,<2)", "hypothesis (>=3.44.24,<4)", "pytest-xdist", "pytest (==5.4.1)", "tox (==3.14.6)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "importlib-metadata" +version = "4.11.2" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "invoke" +version = "1.6.0" +description = "Pythonic task execution" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +requirements_deprecated_finder = ["pip-api", "pipreqs"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "jeepney" +version = "0.7.1" +description = "Low-level, pure Python DBus protocol wrapper." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio", "async-timeout"] +trio = ["trio", "async-generator"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "json-rpc" +version = "1.13.0" +description = "JSON-RPC transport implementation" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "jsonschema" +version = "3.2.0" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0" +six = ">=1.11.0" + +[package.extras] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "keyring" +version = "23.5.0" +description = "Store and access your passwords safely." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +importlib-metadata = ">=3.6" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "lazy-object-proxy" +version = "1.7.1" +description = "A fast and thorough lazy object proxy." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "lru-dict" +version = "1.1.7" +description = "An Dict like LRU container." +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "okota" +version = "0.2.7" +description = "Registries for CIC using the eth-address-index backend" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +chainlib-eth = ">=0.0.15,<0.1.0" +cic-contracts = ">=0.0.5,<0.1.0" +confini = ">=0.5.3,<0.6.0" +eth-accounts-index = ">=0.1.2,<0.2.0" +eth-address-index = ">=0.2.4,<0.3.0" +eth-contract-registry = ">=0.7.2,<0.8.0" +eth_erc20 = ">=0.1.5,<0.2.0" +eth-token-index = ">=0.2.4,<0.3.0" +funga-eth = ">=0.5.1,<0.6.0" + +[package.extras] +testing = ["eth-tester (==0.5.0b2)", "py-evm (==0.3.0a20)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "parsimonious" +version = "0.8.1" +description = "(Soon to be) the fastest pure-Python PEG parser I could muster" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.9.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "phonenumbers" +version = "8.12.12" +description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pkginfo" +version = "1.8.2" +description = "Query metadatdata from sdists / bdists / installed packages." +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +testing = ["coverage", "nose"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "platformdirs" +version = "2.5.1" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "potaahto" +version = "0.1.1" +description = "Conversions and redundancy for poor data formatting choices" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "py-ecc" +version = "4.1.0" +description = "Elliptic curve crypto in python including secp256k1 and alt_bn128" +category = "dev" +optional = false +python-versions = ">=3.5, <4" + +[package.dependencies] +cached-property = ">=1.5.1,<2" +eth-typing = ">=2.1.0,<3.0.0" +eth-utils = ">=1.3.0,<2" +mypy-extensions = ">=0.4.1" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "twine", "pytest (==3.10.1)", "pytest-xdist (==1.26.0)", "flake8 (==3.5.0)", "mypy (==0.641)", "mypy-extensions (>=0.4.1)"] +lint = ["flake8 (==3.5.0)", "mypy (==0.641)", "mypy-extensions (>=0.4.1)"] +test = ["pytest (==3.10.1)", "pytest-xdist (==1.26.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "py-evm" +version = "0.3.0a20" +description = "Python implementation of the Ethereum Virtual Machine" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +blake2b-py = ">=0.1.2,<0.2" +cached-property = ">=1.5.1,<2" +eth-bloom = ">=1.0.3,<2.0.0" +eth-keys = ">=0.2.1,<0.4.0" +eth-typing = ">=2.2.0,<3.0.0" +eth-utils = ">=1.9.4,<2.0.0" +lru-dict = ">=1.1.6" +mypy-extensions = ">=0.4.1,<1.0.0" +py-ecc = ">=1.4.7,<5.0.0" +pyethash = ">=0.1.27,<1.0.0" +rlp = ">=2,<3" +trie = "2.0.0-alpha.5" + +[package.extras] +benchmark = ["termcolor (>=1.1.0,<2.0.0)", "web3 (>=4.1.0,<5.0.0)"] +dev = ["bumpversion (>=0.5.3,<1)", "wheel", "setuptools (>=36.2.0)", "idna (==2.7)", "requests (>=2.20,<3)", "tox (==2.7.0)", "twine", "blake2b-py (>=0.1.2,<0.2)", "cached-property (>=1.5.1,<2)", "eth-bloom (>=1.0.3,<2.0.0)", "eth-keys (>=0.2.1,<0.4.0)", "eth-typing (>=2.2.0,<3.0.0)", "eth-utils (>=1.9.4,<2.0.0)", "lru-dict (>=1.1.6)", "mypy-extensions (>=0.4.1,<1.0.0)", "py-ecc (>=1.4.7,<5.0.0)", "pyethash (>=0.1.27,<1.0.0)", "rlp (>=2,<3)", "trie (==2.0.0-alpha.5)", "coincurve (>=13.0.0,<14.0.0)", "plyvel (>=1.2.0,<2)", "factory-boy (==2.11.1)", "hypothesis (>=5,<6)", "pexpect (>=4.6,<5)", "pytest (>=5.1.3,<6)", "pytest-asyncio (>=0.10.0,<0.11)", "pytest-cov (==2.5.1)", "pytest-watch (>=4.1.0,<5)", "pytest-xdist (==1.31.0)", "py-evm (>=0.2.0-alpha.14)", "pysha3 (>=1.0.0,<2.0.0)", "Sphinx (>=1.5.5,<1.8.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-asyncio (>=0.2.0,<0.3)", "towncrier (>=19.2.0,<20)", "flake8 (==3.8.2)", "flake8-bugbear (==20.1.4)", "mypy (==0.782)", "eth-hash", "eth-hash"] +doc = ["py-evm (>=0.2.0-alpha.14)", "pysha3 (>=1.0.0,<2.0.0)", "Sphinx (>=1.5.5,<1.8.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-asyncio (>=0.2.0,<0.3)", "towncrier (>=19.2.0,<20)"] +eth = ["blake2b-py (>=0.1.2,<0.2)", "cached-property (>=1.5.1,<2)", "eth-bloom (>=1.0.3,<2.0.0)", "eth-keys (>=0.2.1,<0.4.0)", "eth-typing (>=2.2.0,<3.0.0)", "eth-utils (>=1.9.4,<2.0.0)", "lru-dict (>=1.1.6)", "mypy-extensions (>=0.4.1,<1.0.0)", "py-ecc (>=1.4.7,<5.0.0)", "pyethash (>=0.1.27,<1.0.0)", "rlp (>=2,<3)", "trie (==2.0.0-alpha.5)"] +eth-extra = ["coincurve (>=13.0.0,<14.0.0)", "plyvel (>=1.2.0,<2)", "eth-hash", "eth-hash"] +lint = ["flake8 (==3.8.2)", "flake8-bugbear (==20.1.4)", "mypy (==0.782)"] +test = ["factory-boy (==2.11.1)", "hypothesis (>=5,<6)", "pexpect (>=4.6,<5)", "pytest (>=5.1.3,<6)", "pytest-asyncio (>=0.10.0,<0.11)", "pytest-cov (==2.5.1)", "pytest-watch (>=4.1.0,<5)", "pytest-xdist (==1.31.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pycryptodome" +version = "3.10.1" +description = "Cryptographic library for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pyethash" +version = "0.1.27" +description = "Python wrappers for ethash, the ethereum proof of workhashing function" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pygments" +version = "2.11.2" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pylint" +version = "2.12.2" +description = "python code static checker" +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +astroid = ">=2.9.0,<2.10" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.7" +platformdirs = ">=2.2.0" +toml = ">=0.9.2" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pyparsing" +version = "3.0.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pyrsistent" +version = "0.18.1" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pysha3" +version = "1.0.2" +description = "SHA-3 (Keccak) for Python 2.7 - 3.5" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "pytest-cov" +version = "2.10.1" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +coverage = ">=4.4" +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "python-gitlab" +version = "2.10.1" +description = "Interact with GitLab API" +category = "dev" +optional = false +python-versions = ">=3.6.0" + +[package.dependencies] +requests = ">=2.25.0" +requests-toolbelt = ">=0.9.1" + +[package.extras] +autocompletion = ["argcomplete (>=1.10.0,<2)"] +yaml = ["PyYaml (>=5.2)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "python-gnupg" +version = "0.4.7" +description = "A wrapper for the Gnu Privacy Guard (GPG or GnuPG)" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "python-semantic-release" +version = "7.25.2" +description = "Automatic Semantic Versioning for Python projects" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +click = ">=7,<9" +click-log = ">=0.3,<1" +dotty-dict = ">=1.3.0,<2" +gitpython = ">=3.0.8,<4" +invoke = ">=1.4.1,<2" +python-gitlab = ">=1.10,<3" +requests = ">=2.25,<3" +semver = ">=2.10,<3" +tomlkit = "0.7.0" +twine = ">=3,<4" + +[package.extras] +dev = ["tox", "isort", "black"] +docs = ["Sphinx (==1.3.6)"] +mypy = ["mypy", "types-requests"] +test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.13.3)", "mock (==1.3.0)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +description = "" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "readme-renderer" +version = "32.0" +description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +bleach = ">=2.1.0" +docutils = ">=0.13.1" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.5.0,<0.7.0)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "requests" +version = "2.26.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +idna2008 = ["idna"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "rlp" +version = "2.0.1" +description = "A package for Recursive Length Prefix encoding and decoding" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +eth-utils = ">=1.0.2,<2" + +[package.extras] +dev = ["Sphinx (>=1.6.5,<2)", "bumpversion (>=0.5.3,<1)", "flake8 (==3.4.1)", "hypothesis (==5.19.0)", "ipython", "pytest-watch (>=4.1.0,<5)", "pytest-xdist", "pytest (==5.4.3)", "setuptools (>=36.2.0)", "sphinx-rtd-theme (>=0.1.9)", "tox (>=2.9.1,<3)", "twine", "wheel"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"] +lint = ["flake8 (==3.4.1)"] +rust-backend = ["rusty-rlp (>=0.1.15,<0.2)"] +test = ["hypothesis (==5.19.0)", "pytest (==5.4.3)", "tox (>=2.9.1,<3)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "secretstorage" +version = "3.3.1" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "semantic-version" +version = "2.9.0" +description = "A library implementing the 'SemVer' scheme." +category = "dev" +optional = false +python-versions = ">=2.7" + +[package.extras] +dev = ["Django (>=1.11)", "nose2", "tox", "check-manifest", "coverage", "flake8", "wheel", "zest.releaser", "readme-renderer (<25.0)", "colorama (<=0.4.1)"] +doc = ["sphinx", "sphinx-rtd-theme"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "semver" +version = "2.13.0" +description = "Python helper for Semantic Versioning (http://semver.org/)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "setuptools-scm" +version = "6.4.2" +description = "the blessed package to manage your versions by scm tags" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +packaging = ">=20.0" +tomli = ">=1.0.0" + +[package.extras] +test = ["pytest (>=6.2)", "virtualenv (>20)"] +toml = ["setuptools (>=42)"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "smmap" +version = "5.0.0" +description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "tomlkit" +version = "0.7.0" +description = "Style preserving TOML library" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "toolz" +version = "0.11.2" +description = "List processing tools and functional utilities" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "tqdm" +version = "4.63.0" +description = "Fast, Extensible Progress Meter" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +telegram = ["requests"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "trie" +version = "2.0.0a5" +description = "Python implementation of the Ethereum Trie structure" +category = "dev" +optional = false +python-versions = ">=3.6,<4" + +[package.dependencies] +eth-hash = ">=0.1.0,<1.0.0" +eth-utils = ">=1.6.1,<2.0.0" +hexbytes = ">=0.2.0,<0.3.0" +rlp = ">=1,<3" +sortedcontainers = ">=2.1.0,<3" +typing-extensions = ">=3.7.4,<4" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "eth-hash (>=0.1.0,<1.0.0)", "flake8 (==3.8.1)", "hypothesis (>=5.10.4,<6)", "pycryptodome", "pytest-xdist (>=1.31.0,<2)", "tox (>=2.6.0,<3)", "twine", "wheel"] +lint = ["flake8 (==3.8.1)"] +test = ["hypothesis (>=5.10.4,<6)", "pycryptodome", "pytest-xdist (>=1.31.0,<2)", "tox (>=2.6.0,<3)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "twine" +version = "3.8.0" +description = "Collection of utilities for publishing packages on PyPI" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = ">=0.4.3" +importlib-metadata = ">=3.6" +keyring = ">=15.1" +pkginfo = ">=1.8.1" +readme-renderer = ">=21.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +tqdm = ">=4.14" +urllib3 = ">=1.26.0" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "typing-extensions" +version = "3.10.0.2" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "urllib3" +version = "1.26.8" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "vobject" +version = "0.9.6.1" +description = "A full-featured Python package for parsing and creating iCalendar and vCard files" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.4.0" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "websocket-client" +version = "0.57.0" +description = "WebSocket client for Python. hybi13 is supported." +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +six = "*" + +[package.source] +type = "legacy" +url = "https://pip.grassrootseconomics.net" +reference = "grassroots_" + +[[package]] +name = "wrapt" +version = "1.13.3" +description = "Module for decorators, wrappers and monkey patching." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[[package]] +name = "zipp" +version = "3.7.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi_" + +[extras] +eth = ["chainlib-eth", "eth-token-index", "eth-address-index", "okota", "cic_eth_registry", "cic_contracts"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "2f7c01af1a14bcfeedf238b5ac7135e304a9fe162d27ee0638d5551467979be6" + +[metadata.files] +asn1crypto = [] +astroid = [ + {file = "astroid-2.9.3-py3-none-any.whl", hash = "sha256:506daabe5edffb7e696ad82483ad0228245a9742ed7d2d8c9cdb31537decf9f6"}, + {file = "astroid-2.9.3.tar.gz", hash = "sha256:1efdf4e867d4d8ba4a9f6cf9ce07cd182c4c41de77f23814feb27ca93ca9d877"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [] +black = [ + {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, + {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, + {file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"}, + {file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"}, + {file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"}, + {file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"}, + {file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"}, + {file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"}, + {file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"}, + {file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"}, + {file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"}, + {file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"}, + {file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"}, + {file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"}, + {file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"}, + {file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"}, + {file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"}, + {file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"}, + {file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"}, + {file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"}, + {file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"}, + {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"}, + {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"}, +] +blake2b-py = [] +bleach = [ + {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"}, + {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"}, +] +cached-property = [] +cbor2 = [] +certifi = [] +cffi = [ + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, +] +chainlib = [] +chainlib-eth = [] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +cic-contracts = [] +cic-eth-registry = [] +cic-types = [] +click = [ + {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, + {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, +] +click-log = [ + {file = "click-log-0.3.2.tar.gz", hash = "sha256:16fd1ca3fc6b16c98cea63acf1ab474ea8e676849dc669d86afafb0ed7003124"}, + {file = "click_log-0.3.2-py2.py3-none-any.whl", hash = "sha256:eee14dc37cdf3072158570f00406572f9e03e414accdccfccd4c538df9ae322c"}, +] +coincurve = [] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +confini = [] +coverage = [ + {file = "coverage-6.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf"}, + {file = "coverage-6.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac"}, + {file = "coverage-6.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1"}, + {file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4"}, + {file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903"}, + {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c"}, + {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f"}, + {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05"}, + {file = "coverage-6.3.2-cp310-cp310-win32.whl", hash = "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39"}, + {file = "coverage-6.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1"}, + {file = "coverage-6.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa"}, + {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518"}, + {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7"}, + {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6"}, + {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad"}, + {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359"}, + {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4"}, + {file = "coverage-6.3.2-cp37-cp37m-win32.whl", hash = "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca"}, + {file = "coverage-6.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3"}, + {file = "coverage-6.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d"}, + {file = "coverage-6.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059"}, + {file = "coverage-6.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512"}, + {file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca"}, + {file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d"}, + {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0"}, + {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6"}, + {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2"}, + {file = "coverage-6.3.2-cp38-cp38-win32.whl", hash = "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e"}, + {file = "coverage-6.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1"}, + {file = "coverage-6.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620"}, + {file = "coverage-6.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d"}, + {file = "coverage-6.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536"}, + {file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7"}, + {file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2"}, + {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4"}, + {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69"}, + {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684"}, + {file = "coverage-6.3.2-cp39-cp39-win32.whl", hash = "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4"}, + {file = "coverage-6.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92"}, + {file = "coverage-6.3.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf"}, + {file = "coverage-6.3.2.tar.gz", hash = "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9"}, +] +cryptography = [] +cytoolz = [] +docutils = [ + {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, + {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, +] +dotty-dict = [ + {file = "dotty_dict-1.3.0.tar.gz", hash = "sha256:eb0035a3629ecd84397a68f1f42f1e94abd1c34577a19cd3eacad331ee7cbaf0"}, +] +eth-abi = [] +eth-accounts-index = [] +eth-address-index = [] +eth-bloom = [] +eth-contract-registry = [] +eth-erc20 = [ + {file = "eth-erc20-0.1.8.tar.gz", hash = "sha256:5ea7981db30f6316f5f7def8cf69e74425c447493f8938d5541ebbeb79e4319f"}, +] +eth-hash = [] +eth-keys = [ + {file = "eth-keys-0.3.4.tar.gz", hash = "sha256:e5590797f5e2930086c705a6dd1ac14397f74f19bdcd1b5f837475554f354ad8"}, + {file = "eth_keys-0.3.4-py3-none-any.whl", hash = "sha256:565bf62179b8143bcbd302a0ec6c49882d9c7678f9e6ab0484a8a5725f5ef10e"}, +] +eth-tester = [] +eth-token-index = [] +eth-typing = [ + {file = "eth-typing-2.3.0.tar.gz", hash = "sha256:39cce97f401f082739b19258dfa3355101c64390914c73fe2b90012f443e0dc7"}, + {file = "eth_typing-2.3.0-py3-none-any.whl", hash = "sha256:b7fa58635c1cb0cbf538b2f5f1e66139575ea4853eac1d6000f0961a4b277422"}, +] +eth-utils = [] +funga = [] +funga-eth = [] +gitdb = [ + {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, + {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, +] +gitpython = [ + {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, + {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, +] +hexathon = [] +hexbytes = [] +idna = [] +importlib-metadata = [ + {file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"}, + {file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"}, +] +iniconfig = [] +invoke = [ + {file = "invoke-1.6.0-py2-none-any.whl", hash = "sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"}, + {file = "invoke-1.6.0-py3-none-any.whl", hash = "sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317"}, + {file = "invoke-1.6.0.tar.gz", hash = "sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +jeepney = [ + {file = "jeepney-0.7.1-py3-none-any.whl", hash = "sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac"}, + {file = "jeepney-0.7.1.tar.gz", hash = "sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"}, +] +json-rpc = [] +jsonschema = [] +keyring = [ + {file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"}, + {file = "keyring-23.5.0.tar.gz", hash = "sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9"}, +] +lazy-object-proxy = [ + {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, + {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, + {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, + {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, + {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, + {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, + {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, +] +lru-dict = [] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mypy-extensions = [] +okota = [] +packaging = [] +parsimonious = [] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +phonenumbers = [] +pkginfo = [ + {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"}, + {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"}, +] +platformdirs = [ + {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"}, + {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"}, +] +pluggy = [] +potaahto = [] +py = [] +py-ecc = [] +py-evm = [] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pycryptodome = [] +pyethash = [] +pygments = [ + {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, + {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, +] +pylint = [ + {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"}, + {file = "pylint-2.12.2.tar.gz", hash = "sha256:9d945a73640e1fec07ee34b42f5669b770c759acd536ec7b16d7e4b87a9c9ff9"}, +] +pyparsing = [ + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, +] +pyrsistent = [ + {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, + {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, + {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, + {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, + {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, + {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, + {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, + {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, + {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, + {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, + {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, + {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, + {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, + {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, + {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, + {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, +] +pysha3 = [] +pytest = [] +pytest-cov = [] +python-dateutil = [] +python-gitlab = [ + {file = "python-gitlab-2.10.1.tar.gz", hash = "sha256:7afa7d7c062fa62c173190452265a30feefb844428efc58ea5244f3b9fc0d40f"}, + {file = "python_gitlab-2.10.1-py3-none-any.whl", hash = "sha256:581a219759515513ea9399e936ed7137437cfb681f52d2641626685c492c999d"}, +] +python-gnupg = [] +python-semantic-release = [ + {file = "python-semantic-release-7.25.2.tar.gz", hash = "sha256:134294d3ee02a3aa464bf3c00c7777b0c84c6b3332fe234e8b7a087cbf3866d2"}, + {file = "python_semantic_release-7.25.2-py3-none-any.whl", hash = "sha256:8b21bf503486bf13db048501da60362f9ab5adb88435fa431186bcbf24d431ef"}, +] +pywin32-ctypes = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] +readme-renderer = [ + {file = "readme_renderer-32.0-py3-none-any.whl", hash = "sha256:a50a0f2123a4c1145ac6f420e1a348aafefcc9211c846e3d51df05fe3d865b7d"}, + {file = "readme_renderer-32.0.tar.gz", hash = "sha256:b512beafa6798260c7d5af3e1b1f097e58bfcd9a575da7c4ddd5e037490a5b85"}, +] +requests = [] +requests-toolbelt = [ + {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, + {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, +] +rfc3986 = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] +rlp = [] +secretstorage = [ + {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, + {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, +] +semantic-version = [ + {file = "semantic_version-2.9.0-py2.py3-none-any.whl", hash = "sha256:db2504ab37902dd2c9876ece53567aa43a5b2a417fbe188097b2048fff46da3d"}, + {file = "semantic_version-2.9.0.tar.gz", hash = "sha256:abf54873553e5e07a6fd4d5f653b781f5ae41297a493666b59dcf214006a12b2"}, +] +semver = [] +setuptools-scm = [ + {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"}, + {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"}, +] +six = [] +smmap = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] +sortedcontainers = [] +toml = [] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tomlkit = [ + {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, + {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, +] +toolz = [] +tqdm = [ + {file = "tqdm-4.63.0-py2.py3-none-any.whl", hash = "sha256:e643e071046f17139dea55b880dc9b33822ce21613b4a4f5ea57f202833dbc29"}, + {file = "tqdm-4.63.0.tar.gz", hash = "sha256:1d9835ede8e394bb8c9dcbffbca02d717217113adc679236873eeaac5bc0b3cd"}, +] +trie = [] +twine = [ + {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"}, + {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"}, +] +typing-extensions = [] +urllib3 = [] +vobject = [] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +websocket-client = [] +wrapt = [ + {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, + {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, + {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, + {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, + {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, + {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, + {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, + {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, + {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, + {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, + {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, + {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, + {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, + {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, + {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, + {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, + {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, + {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, + {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, +] +zipp = [ + {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, + {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8b63011 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,94 @@ +[tool.poetry] +name = "cic" +version = "0.0.2" +description = "Generic cli tooling for the CIC token network" +authors = [ + "Louis Holbrook ", + "William Luke ", +] +license = "GPL-3.0-or-later" +readme = "README.md" +repository = "https://git.grassecon.net/cicnet/cic-cli" +classifiers = [ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Topic :: Internet", +] +keywords = ["dlt", "blockchain", "cryptocurrency"] +packages = [ + { include = "cic" }, + { include = "cic/runnable/*.py" }, + { include = "cic/ext/**/*.py" }, + { include = "cic/cmd/**/*.py" }, +] + +[tool.poetry.scripts] +cic = 'cic.runnable.cic_cmd:main' + +[[tool.poetry.source]] +name = "grassroots_" +url = "https://pip.grassrootseconomics.net/" + +[[tool.poetry.source]] +name = "pypi_" +url = "https://pypi.org/simple/" + +[tool.poetry.dependencies] +python = "^3.8" +funga-eth = "~0.5.5" +cic-types = "~0.2.1a8" +confini = "~0.5.3" +chainlib = "~0.0.17" +cbor2 = "5.4.1" + +chainlib-eth = { version = "~0.0.25", optional = true } +eth-token-index = { version = "~0.2.4", optional = true } +eth-address-index = { version = "~0.2.4", optional = true } +okota = { version = "~0.2.5", optional = true } +cic_eth_registry = { version = "~0.6.6", optional = true } +cic_contracts = { version = "~0.0.5", optional = true } + + +[tool.poetry.dev-dependencies] +pytest = "6.2.5" +pytest-cov = "2.10.1" +python-semantic-release = "^7.25.2" +pylint = "^2.12.2" +black = { version = "^22.1.0", allow-prereleases = true } +eth-erc20 = ">0.1.2a3,<0.2.0" +eth_tester = "0.5.0b3" +py-evm = "0.3.0a20" +rlp = "2.0.1" + +[tool.poetry.extras] +eth = [ + "chainlib-eth", + "eth-token-index", + "eth-address-index", + "okota", + "cic_eth_registry", + "cic_contracts", +] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +addopts = "--cov=cic --cov-fail-under=40 --cov-report term-missing" +testpaths = ["tests"] + +[tool.semantic_release] +version_variable = ["cic/__init__.py:__version__", "pyproject.toml:version"] +version_source = "commit" +branch = "main" +upload_to_repository = true +upload_to_release = true +build_command = "pip install poetry && poetry build" +hvcs = "gitea" +hvcs_domain = "git.grassecon.net" +check_build_status = false diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 458e323..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -funga-eth~=0.5.1 -cic-types~=0.2.1a5 -confini~=0.5.1 -chainlib~=0.0.13 -cbor2==5.4.1 diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100644 index 6e8c701..0000000 --- a/run_tests.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -a -set -e -set -x -default_pythonpath=$PYTHONPATH:. -export PYTHONPATH=${default_pythonpath:-.} ->&2 echo using pythonpath $PYTHONPATH -for f in `ls tests/*.py`; do - python $f -done -for f in `ls tests/eth/*.py`; do - python $f -done -set +x -set +e -set +a diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index c4e7b40..0000000 --- a/setup.cfg +++ /dev/null @@ -1,32 +0,0 @@ -[metadata] -name = cic -version = 0.0.2 -description = Generic cli tooling for the CIC token network -author = Louis Holbrook -author_email = dev@holbrook.no -url = https://git.grassecon.net/cic-cli.git -keywords = - dlt - blockchain - cryptocurrency -classifiers = - Programming Language :: Python :: 3 - Operating System :: OS Independent - Development Status :: 3 - Alpha - Environment :: Console - Intended Audience :: Developers - License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) - Topic :: Internet -license = GPL3 -licence_files = - LICENSE.txt - - -[options] -python_requires = >= 3.8 -include_package_data = True -packages = - cic - cic.runnable - cic.ext.eth - cic.cmd diff --git a/setup.py b/setup.py deleted file mode 100644 index dceb51a..0000000 --- a/setup.py +++ /dev/null @@ -1,35 +0,0 @@ -from setuptools import setup -import configparser -import os - - -requirements = [] -f = open('requirements.txt', 'r') -while True: - l = f.readline() - if l == '': - break - requirements.append(l.rstrip()) -f.close() - -eth_requirements = [] -f = open('eth_requirements.txt', 'r') -while True: - l = f.readline() - if l == '': - break - eth_requirements.append(l.rstrip()) -f.close() - - -setup( - install_requires=requirements, - extras_require={ - 'eth': eth_requirements, - }, - entry_points={ - 'console_scripts': [ - 'cic-cli=cic.runnable.cic_cmd:main', - ], - }, - ) diff --git a/test_requirements.txt b/test_requirements.txt deleted file mode 100644 index f1d9e50..0000000 --- a/test_requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -eth-erc20>=0.1.2a3,<0.2.0 -eth_tester==0.5.0b3 -py-evm==0.3.0a20 -rlp==2.0.1 -chainlib-eth>=0.0.10a2,<0.1.0 -eth-address-index>=0.2.4a1,<0.3.0 -okota>=0.2.4a6,<0.3.0 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/base_cic.py b/tests/base_cic.py index d05d085..a385373 100644 --- a/tests/base_cic.py +++ b/tests/base_cic.py @@ -1,43 +1,42 @@ # standard imports import os -import tempfile -import logging -import unittest import random +import tempfile +import unittest + +from cic.contract.components.attachment import Attachment +from cic.contract.components.proof import Proof +from cic.contract.processor import ContractProcessor + +# local imports +from cic.writers import KVWriter # external imports from hexathon import add_0x -# local imports -from cic.output import KVWriter -from cic.processor import Processor -from cic.attachment import Attachment -from cic import Proof - test_base_dir = os.path.dirname(os.path.realpath(__file__)) -test_data_dir = os.path.join(test_base_dir, 'testdata') +test_data_dir = os.path.join(test_base_dir, "testdata") -proof_hash = '0f6fc017f29caf512c0feaaf83bc10614b488311cace2973dc248dc24b01e04f' -foo_hash = '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' -bar_hash = 'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9' -root_merged_hash = '795fed550ada0ec1eea4309a282f5910bc3bdb3a9762c7d9cc25d6de71c45096' -root_unmerged_hash = '5dc81e51703e624f498663e7d5d70429b824e9ff60f92b61fe47eb6862a971b4' +proof_hash = "0f6fc017f29caf512c0feaaf83bc10614b488311cace2973dc248dc24b01e04f" +foo_hash = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae" +bar_hash = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" +root_merged_hash = "2a27a488377c753fffea58ad535cfdacc2fcb5cf0ae495ec71d88e31757ec0c3" +root_unmerged_hash = "14dc271290eca763e99c2e7c21c541bded86fb803c6b01bac28cd367db34399c" class TestCICBase(unittest.TestCase): - def setUp(self): super(TestCICBase, self).setUp() random.seed(42) - f = open('/dev/urandom', 'rb') + f = open("/dev/urandom", "rb") addresses = [] - for i in range(3): + for _i in range(3): address_bytes = f.read(32) addresses.append(add_0x(address_bytes.hex())) - self.token_symbol = 'FOO' - + self.token_symbol = "FOO" + token_address_bytes = f.read(20) token_index_address_bytes = f.read(20) address_declarator_address_bytes = f.read(20) @@ -50,23 +49,23 @@ class TestCICBase(unittest.TestCase): self.outputs_dir = tempfile.mkdtemp() self.outputs_writer = KVWriter(self.outputs_dir) - self.core_processor = Processor(outputs_writer=self.outputs_writer) + self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer) self.resources = { - 'token': { - 'reference': self.token_address, - 'key_address': addresses[0], - }, - 'token_index': { - 'reference': self.token_index_address, - 'key_address': addresses[1], - }, - 'address_declarator': { - 'reference': self.address_declarator_address, - 'key_address': addresses[2], - }, - } - proof_dir = os.path.join(test_data_dir, 'proof') + "token": { + "reference": self.token_address, + "key_address": addresses[0], + }, + "token_index": { + "reference": self.token_index_address, + "key_address": addresses[1], + }, + "address_declarator": { + "reference": self.address_declarator_address, + "key_address": addresses[2], + }, + } + proof_dir = os.path.join(test_data_dir, "proof") attach = Attachment(path=proof_dir) attach.load() self.proofs = Proof(proof_dir, attachments=attach) diff --git a/tests/eth/base_eth.py b/tests/eth/base_eth.py index 4d8f04f..e70d8a5 100644 --- a/tests/eth/base_eth.py +++ b/tests/eth/base_eth.py @@ -1,4 +1,4 @@ -# standard imports import unittestimport logging +# standard imports import random import os import logging @@ -24,10 +24,10 @@ from cic_contracts.writer import CICWriter # local imports from cic.ext.eth import CICEth -from cic import Proof -from cic.attachment import Attachment -from cic.output import KVWriter -from cic.processor import Processor +from cic.writers import KVWriter +from cic.contract.processor import ContractProcessor +from cic.contract.components.proof import Proof +from cic.contract.components.attachment import Attachment # test imports @@ -127,4 +127,4 @@ class TestCICEthTokenBase(TestCICEthBase): self.token_precision = 8 self.token_supply = 1073741824 - self.core_processor = Processor(outputs_writer=self.outputs_writer, extensions=[self.adapter]) + self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer, extensions=[self.adapter]) diff --git a/tests/eth/test_eth_full.py b/tests/eth/test_eth_full.py index fcf1caf..373efae 100644 --- a/tests/eth/test_eth_full.py +++ b/tests/eth/test_eth_full.py @@ -27,8 +27,8 @@ from giftable_erc20_token import GiftableToken # local imports from cic.ext.eth import CICEth -from cic.processor import Processor -from cic.token import Token +from cic.contract.processor import ContractProcessor +from cic.contract.components.token import Token # test imports from tests.eth.base_eth import TestCICEthTokenBase @@ -46,7 +46,7 @@ class TestCICEthRPC(TestCICEthTokenBase): gas_oracle = RPCGasOracle(self.rpc) self.adapter = CICEth(self.chain_spec, self.resources, self.proofs, signer=self.signer, rpc=self.rpc, fee_oracle=gas_oracle, outputs_writer=self.outputs_writer) - self.core_processor = Processor(outputs_writer=self.outputs_writer, extensions=[self.adapter]) + self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer, extensions=[self.adapter]) def test_rpc_process_notoken(self): diff --git a/tests/eth/test_eth_offline.py b/tests/eth/test_eth_offline.py index eab7cfc..ad52b14 100644 --- a/tests/eth/test_eth_offline.py +++ b/tests/eth/test_eth_offline.py @@ -5,7 +5,7 @@ import os # local imports from cic.ext.eth import CICEth -from cic.processor import Processor +from cic.contract.processor import ContractProcessor # tests imports from tests.eth.base_eth import TestCICEthBase diff --git a/tests/eth/test_eth_sign.py b/tests/eth/test_eth_sign.py index 63ec81b..15b5c8a 100644 --- a/tests/eth/test_eth_sign.py +++ b/tests/eth/test_eth_sign.py @@ -11,7 +11,7 @@ from hexathon import ( # local imports from cic.ext.eth import CICEth -from cic.processor import Processor +from cic.contract.processor import ContractProcessor # tests imports from tests.eth.base_eth import TestCICEthBase @@ -25,7 +25,7 @@ class TestCICEthSign(TestCICEthBase): def setUp(self): super(TestCICEthSign, self).setUp() self.adapter = CICEth(self.chain_spec, self.resources, self.proofs, signer=self.signer) - self.core_processor = Processor(outputs_writer=self.outputs_writer, extensions=[self.adapter]) + self.core_processor = ContractProcessor(outputs_writer=self.outputs_writer, extensions=[self.adapter]) def test_sign_token_index(self): diff --git a/tests/test_keyfile.py b/tests/test_keyfile.py index 6b29828..89fa1b0 100644 --- a/tests/test_keyfile.py +++ b/tests/test_keyfile.py @@ -1,14 +1,16 @@ # standard imports +import logging import os import unittest -import logging # local imports -from cic.keystore import KeystoreDirectory -from funga.eth.keystore.dict import DictKeystore from funga.error import DecryptError +from funga.eth.keystore.dict import DictKeystore from hexathon import uniform as hex_uniform +# external imports +from cic.keystore import KeystoreDirectory + # test imports from tests.base_cic import test_base_dir @@ -16,8 +18,9 @@ logging = logging.getLogger() script_dir = test_base_dir + def pass_getter(): - return 'test' + return "test" class EthKeystoreDirectory(DictKeystore, KeystoreDirectory): @@ -25,25 +28,25 @@ class EthKeystoreDirectory(DictKeystore, KeystoreDirectory): class TestKeyfile(unittest.TestCase): - def setUp(self): - self.path = os.path.join(script_dir, 'testdata', 'keystore') + self.path = os.path.join(script_dir, "testdata", "keystore") self.keystore = EthKeystoreDirectory() - def test_keystore_bogus(self): - bogus_path = os.path.join(self.path, 'bogus') + bogus_path = os.path.join(self.path, "bogus") self.keystore.process_dir(bogus_path) - def test_keystore_ok(self): - ok_path = os.path.join(self.path, 'ok') + ok_path = os.path.join(self.path, "ok") with self.assertRaises(DecryptError): - self.keystore.process_dir(ok_path) # wrong password - self.keystore.process_dir(ok_path, default_password='test') + self.keystore.process_dir(ok_path) # wrong password + self.keystore.process_dir(ok_path, default_password="test") self.keystore.process_dir(ok_path, password_retriever=pass_getter) - self.assertTrue(hex_uniform('cc4f82F5DacDE395E1E0CFc4d62827C8B8B5688C') in self.keystore.list()) + self.assertTrue( + hex_uniform("cc4f82F5DacDE395E1E0CFc4d62827C8B8B5688C") + in self.keystore.list() + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_meta.py b/tests/test_meta.py index 6858f0b..8897445 100644 --- a/tests/test_meta.py +++ b/tests/test_meta.py @@ -5,27 +5,51 @@ import os # external imports from hexathon import strip_0x - + # local imports -from cic.meta import Meta +from cic.contract.components.meta import Meta # test imports -from tests.base_cic import ( - TestCICBase, - test_data_dir, - ) +from tests.base_cic import TestCICBase, test_data_dir logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() class TestCICMeta(TestCICBase): - def test_meta(self): - fp = os.path.join(test_data_dir, 'proof') + fp = os.path.join(test_data_dir, "proof") m = Meta(fp) m.load() + self.assertEquals( + str(m), + """name = Test +contact.phone = 0700-123456 +country_code = KE +location = Kilifi +""", + ) + + def test_meta_with_initial_values(self): + fp = os.path.join(test_data_dir, "proof") + m = Meta( + fp, + name="TestName", + location="TestLocation", + country_code="TestCC", + contact={ + "phone": "0723578455158", + }, + ) + self.assertEquals( + str(m), + """name = TestName +contact.phone = 0723578455158 +country_code = TestCC +location = TestLocation +""", + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_processor.py b/tests/test_processor.py index 4ccc440..78b6904 100644 --- a/tests/test_processor.py +++ b/tests/test_processor.py @@ -3,17 +3,12 @@ import unittest import logging import os import json -import sys -# external imports -from hexathon import strip_0x - # local imports -from cic import Proof -from cic.processor import Processor -from cic.attachment import Attachment -from cic.meta import Meta -from cic.output import KVWriter +from cic.contract.processor import ContractProcessor +from cic.contract.components.proof import Proof +from cic.contract.components.attachment import Attachment +from cic.contract.components.meta import Meta # test imports from tests.base_cic import ( @@ -28,66 +23,65 @@ logg.setLevel(logging.DEBUG) class MockExt: - def __init__(self, address): self.address = address def process(self): - return (self.address, 'foo') + return (self.address, "foo") class TestCICProcessor(TestCICBase): - def test_processor_meta(self): - fp = os.path.join(test_data_dir, 'proof') + fp = os.path.join(test_data_dir, "proof") m = Meta(fp) m.load() mock_ext = MockExt(self.token_address) - p = Processor(metadata=m, outputs_writer=self.outputs_writer, extensions=[mock_ext]) + p = ContractProcessor( + metadata=m, outputs_writer=self.outputs_writer, extensions=[mock_ext] + ) p.token_address = self.token_address p.process() meta_reference = m.reference(self.token_address) fp = os.path.join(self.outputs_dir, meta_reference) - f = open(fp, 'r') - o = json.load(f) - f.close() + with open(fp, "r", encoding="utf-8") as f: + o = json.load(f) self.assertEqual(m.asdict(), o) - def test_processor_attachment(self): - fp = os.path.join(test_data_dir, 'proof') + fp = os.path.join(test_data_dir, "proof") m = Attachment(fp) m.load() mock_ext = MockExt(self.token_address) - p = Processor(attachment=m, outputs_writer=self.outputs_writer, extensions=[mock_ext]) + p = ContractProcessor( + attachment=m, outputs_writer=self.outputs_writer, extensions=[mock_ext] + ) p.process() - - for k in list(m.contents.keys()): + for _k in list(m.contents.keys()): os.stat(fp) - def test_processor_proof_noattachment(self): - fp = os.path.join(test_data_dir, 'proof') + fp = os.path.join(test_data_dir, "proof") m = Proof(fp) - ap = os.path.join(test_data_dir, 'proof_empty') + ap = os.path.join(test_data_dir, "proof_empty") m.extra_attachments = Attachment(ap) m.load() mock_ext = MockExt(self.token_address) - p = Processor(proof=m, outputs_writer=self.outputs_writer, extensions=[mock_ext]) + p = ContractProcessor( + proof=m, outputs_writer=self.outputs_writer, extensions=[mock_ext] + ) p.process() - self.assertEqual(p.outputs[0], root_unmerged_hash) - + self.assertEqual(p.outputs[0], root_unmerged_hash) def test_processor_proof_attachment(self): - fp = os.path.join(test_data_dir, 'proof') + fp = os.path.join(test_data_dir, "proof") ma = Attachment(fp) ma.load() @@ -96,11 +90,13 @@ class TestCICProcessor(TestCICBase): mp.load() mock_ext = MockExt(self.token_address) - p = Processor(proof=mp, outputs_writer=self.outputs_writer, extensions=[mock_ext]) + p = ContractProcessor( + proof=mp, outputs_writer=self.outputs_writer, extensions=[mock_ext] + ) p.process() - self.assertEqual(p.outputs[0], root_merged_hash) - + self.assertEqual(p.outputs[0], root_merged_hash) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_proof.py b/tests/test_proof.py index 4e31018..d04fb69 100644 --- a/tests/test_proof.py +++ b/tests/test_proof.py @@ -4,23 +4,35 @@ import unittest import logging # local imports -from cic import Proof -from cic.attachment import Attachment +from cic.contract.components.proof import Proof +from cic.contract.components.attachment import Attachment # test imports -from tests.base_cic import ( - test_data_dir, - TestCICBase, - root_merged_hash, - ) +from tests.base_cic import test_data_dir, TestCICBase, root_merged_hash logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() class TestProof(TestCICBase): + def test_proof_load(self): + proof_path = os.path.join(test_data_dir, "proof") + attach = Attachment(proof_path, writer=self.outputs_writer) + attach.load() + c = Proof(path=proof_path, attachments=attach) + c.load() + self.assertEquals( + str(c), + """description = foo bar baz +issuer = the man +namespace = ge +version = 0 +proofs[0] = 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae +proofs[1] = fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9 +""", + ) def test_proof_serialize_merge(self): - proof_path = os.path.join(test_data_dir, 'proof') + proof_path = os.path.join(test_data_dir, "proof") attach = Attachment(proof_path, writer=self.outputs_writer) attach.load() @@ -31,5 +43,5 @@ class TestProof(TestCICBase): self.assertEqual(v, root_merged_hash) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_output.py b/tests/test_writers.py similarity index 94% rename from tests/test_output.py rename to tests/test_writers.py index cae84bb..c6e8be4 100644 --- a/tests/test_output.py +++ b/tests/test_writers.py @@ -7,7 +7,7 @@ import logging from hexathon import strip_0x # local imports -from cic.output import KVWriter +from cic.writers import KVWriter # test imports from tests.base_cic import TestCICBase diff --git a/tests/testdata/proof/meta.json b/tests/testdata/proof/meta.json index 0023838..f961905 100644 --- a/tests/testdata/proof/meta.json +++ b/tests/testdata/proof/meta.json @@ -1,7 +1,8 @@ { - "name": "", - "location": "", - "country_code": "", + "name": "Test", + "location": "Kilifi", + "country_code": "KE", "contact": { + "phone": "0700-123456" } } -- 2.45.2 From 4f219e3d1853befa197f46a19dc8a8a76ef26811 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 11:17:17 +0300 Subject: [PATCH 02/19] fix: broken imports --- cic/cmd/export.py | 16 +- cic/cmd/ext.py | 2 +- cic/cmd/init.py | 10 +- cic/cmd/show.py | 10 +- cic/cmd/wizard.py | 260 ++--------------------------- cic/contract/base.py | 2 +- cic/contract/components/meta.py | 26 --- cic/contract/components/network.py | 27 ++- cic/contract/contract.py | 34 ++-- cic/contract/helpers.py | 1 + cic/writers.py | 64 ++++--- config/dev-docker/config.ini | 6 +- config/prod/config.ini | 4 +- 13 files changed, 116 insertions(+), 346 deletions(-) diff --git a/cic/cmd/export.py b/cic/cmd/export.py index 33a714c..384d45a 100644 --- a/cic/cmd/export.py +++ b/cic/cmd/export.py @@ -4,17 +4,19 @@ import logging import os from typing import Optional -# local imports -from cic import ContractProcessor, Proof -from cic.attachment import Attachment -from cic.meta import Meta, MetadataWriter -from cic.network import Network -from cic.writers import HTTPWriter, KeyedWriterFactory -from cic.token import Token # external imports from cic_types.ext.metadata import MetadataRequestsHandler from cic_types.ext.metadata.signer import Signer as MetadataSigner +# local imports +from cic.contract.processor import ContractProcessor +from cic.contract.components.proof import Proof +from cic.contract.components.attachment import Attachment +from cic.contract.components.meta import Meta +from cic.contract.components.network import Network +from cic.contract.components.token import Token +from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter + logg = logging.getLogger(__name__) diff --git a/cic/cmd/ext.py b/cic/cmd/ext.py index 3f02d73..8576d29 100644 --- a/cic/cmd/ext.py +++ b/cic/cmd/ext.py @@ -4,7 +4,7 @@ import importlib # external imports from chainlib.chain import ChainSpec # local imports -from cic.network import Network +from cic.contract.components.network import Network def process_args(argparser): diff --git a/cic/cmd/init.py b/cic/cmd/init.py index a43e49e..092a50c 100644 --- a/cic/cmd/init.py +++ b/cic/cmd/init.py @@ -3,11 +3,11 @@ import logging import os # local imports -from cic import Proof -from cic.meta import Meta -from cic.attachment import Attachment -from cic.network import Network -from cic.token import Token +from cic.contract.components.proof import Proof +from cic.contract.components.meta import Meta +from cic.contract.components.attachment import Attachment +from cic.contract.components.network import Network +from cic.contract.components.token import Token logg = logging.getLogger(__name__) diff --git a/cic/cmd/show.py b/cic/cmd/show.py index 8186842..b963706 100644 --- a/cic/cmd/show.py +++ b/cic/cmd/show.py @@ -1,9 +1,9 @@ # local imports -from cic import Proof -from cic.meta import Meta -from cic.attachment import Attachment -from cic.network import Network -from cic.token import Token +from cic.contract.components.proof import Proof +from cic.contract.components.meta import Meta +from cic.contract.components.attachment import Attachment +from cic.contract.components.network import Network +from cic.contract.components.token import Token def process_args(argparser): diff --git a/cic/cmd/wizard.py b/cic/cmd/wizard.py index 82336ba..4d6edfd 100644 --- a/cic/cmd/wizard.py +++ b/cic/cmd/wizard.py @@ -13,13 +13,12 @@ import requests from chainlib.chain import ChainSpec # local imports -from cic import Proof -from cic.actions.deploy import deploy -from cic.actions.types import Contract, Options -from cic.attachment import Attachment -from cic.meta import Meta -from cic.network import Network -from cic.token import Token +from cic.contract.components.proof import Proof +from cic.contract.components.attachment import Attachment +from cic.contract.components.meta import Meta +from cic.contract.components.network import Network +from cic.contract.components.token import Token +from cic.contract.contract import generate_contract, load_contract, deploy_contract if TYPE_CHECKING: from chainlib.cli.config import Config @@ -62,237 +61,8 @@ def validate_args(_args): pass -CONTRACTS = [ - { - "url": "https://gitlab.com/cicnet/eth-erc20/-/raw/master/python/giftable_erc20_token/data/GiftableToken", - "name": "Giftable Token", - }, - { - "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", - "name": "Demurrage Token Single No Cap", - }, -] -# Download File from Url -def download_file(url: str, directory: str, filename=None) -> (str, bytes): - os.makedirs(directory, exist_ok=True) - filename = filename if filename else url.split("/")[-1] - path = os.path.join(directory, filename) - if not os.path.exists(path): - log.debug(f"Downloading {filename}") - r = requests.get(url, allow_redirects=True) - open(path, "wb").write(r.content) - return path - return path - - -def get_contract_args(data: list): - for item in data: - if item["type"] == "constructor": - return item["inputs"] - raise Exception("No constructor found in contract") - - -def print_contract_args(json_path: str): - json_data = json.load(open(json_path, encoding="utf-8")) - print("Contract Args:") - for contract_arg in get_contract_args(json_data): - print( - f"\t{contract_arg.get('name', '')} - {contract_arg.get('type', '')}" - ) - - -def select_contract(): - print("Contracts:") - print("\t C - Custom (path/url to contract)") - for idx, contract in enumerate(CONTRACTS): - print(f"\t {idx} - {contract['name']}") - - val = input("Select contract (C,0,1..): ") - if val.isdigit() and int(val) < len(CONTRACTS): - contract = CONTRACTS[int(val)] - directory = f"./contracts/{contract['name']}" - bin_path = os.path.abspath(download_file(contract["url"] + ".bin", directory)) - json_path = download_file(contract["url"] + ".json", directory) - elif val == "C": - possible_bin_location = input("Enter path/url to contract: ") - # possible_bin_location is path - if possible_bin_location[0] == "." or possible_bin_location[0] == "/": - if os.path.exists(possible_bin_location): - bin_path = os.path.abspath(possible_bin_location) - else: - raise Exception(f"File {possible_bin_location} does not exist") - - possible_json_path = val.replace(".bin", ".json") - if os.path.exists(possible_json_path): - json_path = possible_json_path - # possible_bin_location is url - else: - bin_path = download_file(possible_bin_location, directory) - else: - print("Invalid selection") - exit(1) - contract_extra_args = [] - contract_extra_args_types = [] - - if os.path.exists(json_path): - json_data = json.load(open(json_path, encoding="utf-8")) - for contract_arg in get_contract_args(json_data): - arg_name = contract_arg.get("name") - arg_type = contract_arg.get("type") - if arg_name not in ["_decimals", "_name", "_symbol"]: - val = input(f"Enter value for {arg_name} ({arg_type}): ") - contract_extra_args.append(val) - if arg_type == "uint128": - contract_extra_args_types.append("uint256") - else: - contract_extra_args_types.append(arg_type) - - return { - "bin_path": bin_path, - "json_path": json_path, - "extra_args": contract_extra_args, - "extra_args_types": contract_extra_args_types, - } - - -def init_token(directory: str, code=""): - contract = select_contract() - code = contract["bin_path"] - contract_extra_args = contract["extra_args"] - contract_extra_args_types = contract["extra_args_types"] - - name = input("Enter Token Name (Foo Token): ") or "Foo Token" - symbol = input("Enter Token Symbol (FOO): ") or "FOO" - precision = input("Enter Token Precision (6): ") or 6 - supply = input("Enter Token Supply (0): ") or 0 - - contract_token = Token( - directory, - name=name, - symbol=symbol, - precision=precision, - extra_args=contract_extra_args, - extra_args_types=contract_extra_args_types, - supply=supply, - code=code, - ) - contract_token.start() - return contract_token - - -def init_proof(directory): - description = input("Enter Proof Description (None): ") or None - namespace = input("Enter Proof Namespace (ge): ") or "ge" - issuer = input("Enter Proof Issuer (None): ") or None - contract_proof = Proof(directory, description, namespace, issuer) - contract_proof.start() - return contract_proof - - -def init_meta(directory): - name = input("Enter Name (None): ") or "" - country_code = input("Enter Country Code (KE): ") or "KE" - location = input("Enter Location (None): ") or "" - adding_contact_info = True - contact = {} - while adding_contact_info: - value = input("Enter contact info (e.g 'phone: +254723522718'): ") or None - if value: - data = value.split(":") - if len(data) != 2: - print("Invalid contact info, you must enter in the format 'key: value'") - continue - contact[data[0].strip()] = data[1].strip() - else: - adding_contact_info = False - contract_meta = Meta( - directory, - name=name, - country_code=country_code, - location=location, - contact=contact, - ) - contract_meta.start() - return contract_meta - - -def init_attachment(directory): - contract_attchment = Attachment(directory) - contract_attchment.start() - input( - f"Please add attachment files to '{os.path.abspath(os.path.join(directory,'attachments'))}' and then press ENTER to continue" - ) - contract_attchment.load() - return contract_attchment - - -def load_contract(directory) -> Contract: - token = Token(path=directory) - proof = Proof(path=directory) - meta = Meta(path=directory) - attachment = Attachment(path=directory) - network = Network(directory) - - token.load() - proof.load() - meta.load() - attachment.load() - network.load() - return Contract( - token=token, proof=proof, meta=meta, attachment=attachment, network=network - ) - - -def init_network( - directory, - options: Options, - targets: List[str], -): - contract_network = Network(directory, targets=targets) - contract_network.start() - - for target in targets: - m = importlib.import_module(f"cic.ext.{target}.start") - m.extension_start( - contract_network, - registry_address=options.contract_registry, - chain_spec=options.chain_spec, - rpc_provider=options.rpc_provider, - key_account_address=options.key_account, - ) - contract_network.load() - return contract_network - - -def generate(directory: str, target: str, options: Options) -> Contract: - if os.path.exists(directory): - contine = input( - "Directory already exists, Would you like to delete it? (y/n): " - ) - if contine.lower() != "y": - print("Exiting") - exit(1) - else: - print(f"Deleted {directory}") - os.system(f"rm -rf {directory}") - os.makedirs(directory) - - token = init_token(directory) - proof = init_proof(directory) - meta = init_meta(directory) - attachment = init_attachment(directory) - network = init_network( - directory, - options, - targets=[target], - ) - return Contract( - token=token, proof=proof, meta=meta, attachment=attachment, network=network - ) - - -def get_options(config: Config, eargs) -> Options: +def get_options(config: Config, eargs): # Defaults default_contract_registry = config.get( "CIC_REGISTRY_ADDRESS" @@ -333,7 +103,7 @@ def get_options(config: Config, eargs) -> Options: auth_keyfile_path = config.get("AUTH_KEYFILE_PATH") auth_db_path = config.get("AUTH_DB_PATH") - options = Options( + options = [ auth_db_path, auth_keyfile_path, auth_passphrase, @@ -344,14 +114,11 @@ def get_options(config: Config, eargs) -> Options: metadata_endpoint, default_wallet_keyfile, default_wallet_passphrase, - ) + ] print(options) return options - - - ExtraArgs = {"skip_gen": str, "skip_deploy": str, "target": str, "path": str, "p": str} @@ -362,22 +129,19 @@ def execute(config, eargs: ExtraArgs): skip_gen = eargs.skip_gen skip_deploy = eargs.skip_deploy - options = get_options(config, eargs) - if not skip_gen: - contract = generate(directory, target, options) + contract = generate_contract(directory, [target], config, interactive=True) else: contract = load_contract(directory) - print_contract(contract) + print(contract) if not skip_deploy: ready_to_deploy = input("Ready to deploy? (y/n): ") if ready_to_deploy == "y": - deploy( + deploy_contract( config=config, contract_directory=directory, - options=options, target=target, ) print("Deployed") diff --git a/cic/contract/base.py b/cic/contract/base.py index f8c2c3a..3c2edda 100644 --- a/cic/contract/base.py +++ b/cic/contract/base.py @@ -3,7 +3,7 @@ import os import hashlib -mod_dir = os.path.dirname(os.path.realpath(__file__)) +mod_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..') root_dir = os.path.join(mod_dir, '..') data_dir = os.path.join(mod_dir, 'data') schema_dir = os.path.join(mod_dir, 'schema') diff --git a/cic/contract/components/meta.py b/cic/contract/components/meta.py index 20e1bab..f8ca127 100644 --- a/cic/contract/components/meta.py +++ b/cic/contract/components/meta.py @@ -4,8 +4,6 @@ from __future__ import annotations import os import json import logging -import base64 -from typing import TYPE_CHECKING # external imports from cic_types import MetadataPointer @@ -14,8 +12,6 @@ from hexathon import strip_0x # local imports from cic.contract.base import Data, data_dir -from cic.writers import OutputWriter -from cic_types.ext.metadata import MetadataRequestsHandler from cic.utils import object_to_str logg = logging.getLogger(__name__) @@ -139,25 +135,3 @@ class Meta(Data): def __str__(self): return object_to_str(self, ["name", "contact", "country_code", "location"]) - -class MetadataWriter(OutputWriter): - """Custom writer for publishing data under immutable content-addressed pointers in the cic-meta storage backend. - - Data that is not utf-8 will be converted to base64 before publishing. - - Implements cic.writers.OutputWriter - """ - - def write(self, k, v): - rq = MetadataRequestsHandler(MetadataPointer.NONE, bytes.fromhex(k)) - try: - v = v.decode("utf-8") - v = json.loads(v) - logg.debug(f"metadatawriter bindecode {k} {v}") - except UnicodeDecodeError: - v = base64.b64encode(v).decode("utf-8") - v = json.loads(json.dumps(v, separators=(",", ":"))) - logg.debug(f"metadatawriter b64encode {k} {v}") - r = rq.create(v) - logg.info(f"metadata submitted at {k}") - return r diff --git a/cic/contract/components/network.py b/cic/contract/components/network.py index c77bb46..650dfd3 100644 --- a/cic/contract/components/network.py +++ b/cic/contract/components/network.py @@ -7,7 +7,7 @@ import logging from chainlib.chain import ChainSpec # local imports -from cic.contract.components.base import Data, data_dir +from cic.contract.base import Data, data_dir logg = logging.getLogger(__name__) @@ -35,9 +35,8 @@ class Network(Data): """ super(Network, self).load() - f = open(self.network_path, 'r') - o = json.load(f) - f.close() + with open(self.network_path, 'r', encoding='utf-8') as f: + o = json.load(f) self.resources = o['resources'] @@ -53,9 +52,8 @@ class Network(Data): network_template_file_path = os.path.join(data_dir, f'network_template_v{self.version()}.json') - f = open(network_template_file_path) - o_part = json.load(f) - f.close() + with open(network_template_file_path, encoding='utf-8') as f: + o_part = json.load(f) self.resources = {} for v in self.targets: @@ -67,11 +65,10 @@ class Network(Data): def save(self): """Save network settings to file. """ - f = open(self.network_path, 'w') - json.dump({ - 'resources': self.resources, - }, f, sort_keys=True, indent="\t") - f.close() + with open(self.network_path, 'w', encoding='utf-8') as f: + json.dump({ + 'resources': self.resources, + }, f, sort_keys=True, indent="\t") def resource(self, k): @@ -83,8 +80,8 @@ class Network(Data): :return: Extension settings """ v = self.resources.get(k) - if v == None: - raise AttributeError('no defined reference for {}'.format(k)) + if v is None: + raise AttributeError(f'No defined reference for {k}') return v @@ -129,7 +126,7 @@ class Network(Data): """ chain_spec_dict = chain_spec.asdict() for k in chain_spec_dict.keys(): - logg.debug('resources {}'.format(self.resources)) + logg.debug(f'resources: {self.resources}') self.resources[resource_key]['chain_spec'][k] = chain_spec_dict[k] diff --git a/cic/contract/contract.py b/cic/contract/contract.py index b11fce0..cf08ab9 100644 --- a/cic/contract/contract.py +++ b/cic/contract/contract.py @@ -10,16 +10,16 @@ import requests from cic_types.ext.metadata import MetadataRequestsHandler from cic_types.ext.metadata.signer import Signer as MetadataSigner from chainlib.cli.config import Config - +from chainlib.chain import ChainSpec # Local Modules -from cic.contract import ContractProcessor +from cic.contract.processor import ContractProcessor from cic.contract.components.attachment import Attachment from cic.contract.components.meta import Meta from cic.contract.components.network import Network from cic.contract.components.proof import Proof from cic.contract.components.token import Token from cic.contract.helpers import init_writers_from_config -from cic.writers import HTTPWriter, KeyedWriterFactory, OutputWriter +from cic.writers import HTTPWriter, KeyedWriterFactory, OutputWriter, MetadataWriter log = logging.getLogger(__name__) @@ -78,35 +78,45 @@ def generate_contract( "Directory already exists, Would you like to delete it? (y/n): " ) if contine.lower() != "y": - print("Exiting") - exit(1) + print("Trying to load existing contract") + return load_contract(directory) else: print(f"Deleted {directory}") os.system(f"rm -rf {directory}") os.makedirs(directory) - + log.debug("Generating token") token = Token(directory, interactive=interactive) token.start() - + + log.debug("Generating proof") proof = Proof(directory, interactive=interactive) proof.start() + log.debug("Generating meta") meta = Meta(directory, interactive=interactive) meta.start() + log.debug("Generating attachment") attachment = Attachment(directory, interactive=interactive) + log.debug("Generating network") network = Network(directory, targets=targets) network.start() + log.debug(f"""Populating infomation from network: + CIC_REGISTRY_ADDRESS: {config.get("CIC_REGISTRY_ADDRESS")} + CHAIN_SPEC: {config.get("CHAIN_SPEC")} + RPC_PROVIDER: {config.get("RPC_PROVIDER")} + AUTH_KEY: {config.get("AUTH_KEY")} + """) for target in targets: m = importlib.import_module(f"cic.ext.{target}.start") m.extension_start( network, registry_address=config.get("CIC_REGISTRY_ADDRESS"), - chain_spec=config.get("CHAIN_SPEC"), + chain_spec=ChainSpec.from_chain_str(config.get("CHAIN_SPEC")), rpc_provider=config.get("RPC_PROVIDER"), - key_account_address=config.get("RPC_PROVIDER"), + key_account_address=config.get("AUTH_KEY"), # TODO this should come from the wallet keystore ) network.load() @@ -115,7 +125,7 @@ def generate_contract( ) -def deploy( +def deploy_contract( config: Config, target: str, contract_directory: str, @@ -133,7 +143,7 @@ def deploy( if metadata_endpoint is not None: MetadataRequestsHandler.base_url = metadata_endpoint MetadataSigner.gpg_path = "/tmp" - MetadataSigner.key_file_path = config.get("AUTH_KEYFILE") + MetadataSigner.key_file_path = config.get("AUTH_KEYFILE_PATH") MetadataSigner.gpg_passphrase = config.get("AUTH_PASSPHRASE") writers["proof"] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new writers["attachment"] = KeyedWriterFactory(None, HTTPWriter).new @@ -169,6 +179,8 @@ def deploy( chain_spec = cn.chain_spec config.add(chain_spec, "CHAIN_SPEC", exists_ok=True) log.debug(f"using CHAIN_SPEC: {str(chain_spec)} from network") + print(chain_spec) + signer_hint = config.get("WALLET_KEY_FILE") (rpc, signer) = cmd_mod.parse_adapter(config, signer_hint) diff --git a/cic/contract/helpers.py b/cic/contract/helpers.py index 93b65d1..3ec71b5 100644 --- a/cic/contract/helpers.py +++ b/cic/contract/helpers.py @@ -4,6 +4,7 @@ import logging import sys import json import requests +import importlib # local imports from cic.writers import OutputWriter diff --git a/cic/writers.py b/cic/writers.py index a84a9a7..3a139e3 100644 --- a/cic/writers.py +++ b/cic/writers.py @@ -1,14 +1,18 @@ # standard imports +import base64 +import logging import os import sys -import logging import urllib.request +from typing import TYPE_CHECKING + +from cic_types.ext.metadata import MetadataRequestsHandler + logg = logging.getLogger(__name__) class OutputWriter: - def __init__(self, *args, **kwargs): pass @@ -17,13 +21,11 @@ class OutputWriter: class StdoutWriter(OutputWriter): - def write(self, k, v): - sys.stdout.write('{}\t{}\n'.format(k, v)) + sys.stdout.write("{}\t{}\n".format(k, v)) class KVWriter(OutputWriter): - def __init__(self, path=None, *args, **kwargs): try: os.stat(path) @@ -31,43 +33,38 @@ class KVWriter(OutputWriter): os.makedirs(path) self.path = path - def write(self, k, v): fp = os.path.join(self.path, str(k)) - logg.debug('path write {} {}'.format(fp, str(v))) - f = open(fp, 'wb') + logg.debug("path write {} {}".format(fp, str(v))) + f = open(fp, "wb") f.write(v) f.close() class HTTPWriter(OutputWriter): - def __init__(self, path=None, *args, **kwargs): super(HTTPWriter, self).__init__(*args, **kwargs) self.path = path - def write(self, k, v): path = self.path if k != None: path = os.path.join(path, k) - logg.debug(f'http writer post {path} \n key: {k}, value: {v}') - rq = urllib.request.Request(path, method='POST', data=v) + logg.debug(f"http writer post {path} \n key: {k}, value: {v}") + rq = urllib.request.Request(path, method="POST", data=v) r = urllib.request.urlopen(rq) - logg.info('http writer submitted at {}'.format(r.read())) + logg.info("http writer submitted at {}".format(r.read())) class KeyedWriter(OutputWriter): - def __init__(self, writer_keyed, writer_immutable): self.writer_keyed = writer_keyed self.writer_immutable = writer_immutable - def write(self, key, value): - logg.debug(f'writing keywriter key: {key} value: {value}') + logg.debug(f"writing keywriter key: {key} value: {value}") if isinstance(value, str): - value = value.encode('utf-8') + value = value.encode("utf-8") if self.writer_keyed != None: self.writer_keyed.write(key, value) if self.writer_immutable != None: @@ -75,15 +72,15 @@ class KeyedWriter(OutputWriter): class KeyedWriterFactory: - - def __init__(self, key_writer_constructor, immutable_writer_constructor, *args, **kwargs): + def __init__( + self, key_writer_constructor, immutable_writer_constructor, *args, **kwargs + ): self.key_writer_constructor = key_writer_constructor self.immutable_writer_constructor = immutable_writer_constructor self.x = {} for k in kwargs.keys(): - logg.debug('adding key {} t keyed writer factory'.format(k)) - self.x[k] = kwargs[k] - + logg.debug("adding key {} t keyed writer factory".format(k)) + self.x[k] = kwargs[k] def new(self, path=None, *args, **kwargs): writer_keyed = None @@ -93,3 +90,26 @@ class KeyedWriterFactory: if self.immutable_writer_constructor != None: writer_immutable = self.immutable_writer_constructor(path, **self.x) return KeyedWriter(writer_keyed, writer_immutable) + + +class MetadataWriter(OutputWriter): + """Custom writer for publishing data under immutable content-addressed pointers in the cic-meta storage backend. + + Data that is not utf-8 will be converted to base64 before publishing. + + Implements cic.writers.OutputWriter + """ + + def write(self, k, v): + rq = MetadataRequestsHandler(MetadataPointer.NONE, bytes.fromhex(k)) + try: + v = v.decode("utf-8") + v = json.loads(v) + logg.debug(f"metadatawriter bindecode {k} {v}") + except UnicodeDecodeError: + v = base64.b64encode(v).decode("utf-8") + v = json.loads(json.dumps(v, separators=(",", ":"))) + logg.debug(f"metadatawriter b64encode {k} {v}") + r = rq.create(v) + logg.info(f"metadata submitted at {k}") + return r diff --git a/config/dev-docker/config.ini b/config/dev-docker/config.ini index bf92761..a1e19ca 100644 --- a/config/dev-docker/config.ini +++ b/config/dev-docker/config.ini @@ -16,13 +16,13 @@ provider = http://localhost:63545 [auth] type = gnupg -db_path = ~/.local/share/cic/clicada +db_path = /home/will/.local/share/cic/clicada key = eb3907ecad74a0013c259d5874ae7f22dcbcc95c -keyfile_path = ~/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc +keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc passphrase = merman [wallet] -key_file = ~/grassroots/cic-internal-integration/apps/contract-migration/keystore/UTC +key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore passphrase = [chain] diff --git a/config/prod/config.ini b/config/prod/config.ini index 45a2e48..cd27599 100644 --- a/config/prod/config.ini +++ b/config/prod/config.ini @@ -16,9 +16,9 @@ provider = https://rpc.grassecon.net [auth] type = gnupg -db_path = ~/.local/share/cic/clicada +db_path = /home/will/.local/share/cic/clicada key = CCE2E1D2D0E36ADE0405E2D0995BB21816313BD5 -keyfile_path = ~/.config/cic/staff-client/user.asc +keyfile_path = /home/will/.config/cic/staff-client/user.asc passphrase = queenmarlena [wallet] -- 2.45.2 From be5d988fa4d03dfcd44f71c7c6d4a562b780da09 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 11:22:14 +0300 Subject: [PATCH 03/19] docs: add badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e8889c2..a86d19a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # CIC Token Deployment Tool +[![Status](https://ci.grassecon.net/api/badges/grassrootseconomics/cic/status.svg?ref=refs/heads/master)](https://ci.grassecon.net/grassrootseconomics/cic) +[![Version](https://img.shields.io/pypi/v/cic?color=green)](https://pypi.org/project/cic/) CIC-CLI provides tooling to generate and publish metadata in relation to token deployments. -- 2.45.2 From 264abf4138d310a2fde50c3ecdc2f5d4649b4afe Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 11:31:22 +0300 Subject: [PATCH 04/19] chore: move network out of contact components --- cic/cmd/export.py | 4 ++-- cic/cmd/ext.py | 28 +++++++++++++++--------- cic/cmd/init.py | 24 +++++++++++++------- cic/cmd/show.py | 21 +++++++++++++----- cic/cmd/wizard.py | 3 +-- cic/contract/contract.py | 17 +++++++++----- cic/contract/{components => }/network.py | 3 +-- 7 files changed, 65 insertions(+), 35 deletions(-) rename cic/contract/{components => }/network.py (99%) diff --git a/cic/cmd/export.py b/cic/cmd/export.py index 384d45a..5199967 100644 --- a/cic/cmd/export.py +++ b/cic/cmd/export.py @@ -10,10 +10,10 @@ from cic_types.ext.metadata.signer import Signer as MetadataSigner # local imports from cic.contract.processor import ContractProcessor -from cic.contract.components.proof import Proof +from cic.contract.components.proof import Proof from cic.contract.components.attachment import Attachment from cic.contract.components.meta import Meta -from cic.contract.components.network import Network +from cic.contract.network import Network from cic.contract.components.token import Token from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter diff --git a/cic/cmd/ext.py b/cic/cmd/ext.py index 8576d29..d015a0e 100644 --- a/cic/cmd/ext.py +++ b/cic/cmd/ext.py @@ -3,16 +3,21 @@ import importlib # external imports from chainlib.chain import ChainSpec + # local imports -from cic.contract.components.network import Network +from cic.contract.network import Network def process_args(argparser): - argparser.add_argument('--registry', required=True, type=str, help='contract registry address') - argparser.add_argument('-d', '--directory', type=str, dest='directory', default='.', help='directory') - argparser.add_argument('-p', type=str, help='RPC endpoint') - argparser.add_argument('-i', type=str, help='chain spec string') - argparser.add_argument('target', help='target to initialize') + argparser.add_argument( + "--registry", required=True, type=str, help="contract registry address" + ) + argparser.add_argument( + "-d", "--directory", type=str, dest="directory", default=".", help="directory" + ) + argparser.add_argument("-p", type=str, help="RPC endpoint") + argparser.add_argument("-i", type=str, help="chain spec string") + argparser.add_argument("target", help="target to initialize") def validate_args(args): @@ -23,8 +28,11 @@ def execute(config, eargs): cn = Network(eargs.directory, targets=eargs.target) cn.load() - chain_spec = ChainSpec.from_chain_str(eargs.i) - m = importlib.import_module(f'cic.ext.{eargs.target}.start') - m.extension_start(cn, registry_address=eargs.registry, chain_spec=chain_spec, rpc_provider=config.get('RPC_PROVIDER')) - + m = importlib.import_module(f"cic.ext.{eargs.target}.start") + m.extension_start( + cn, + registry_address=eargs.registry, + chain_spec=chain_spec, + rpc_provider=config.get("RPC_PROVIDER"), + ) diff --git a/cic/cmd/init.py b/cic/cmd/init.py index 092a50c..3226a8e 100644 --- a/cic/cmd/init.py +++ b/cic/cmd/init.py @@ -6,18 +6,24 @@ import os from cic.contract.components.proof import Proof from cic.contract.components.meta import Meta from cic.contract.components.attachment import Attachment -from cic.contract.components.network import Network +from cic.contract.network import Network from cic.contract.components.token import Token logg = logging.getLogger(__name__) def process_args(argparser): - argparser.add_argument('--target', action='append', type=str, default=[], help='initialize network specification file with target') - argparser.add_argument('--name', type=str, help='token name') - argparser.add_argument('--symbol', type=str, help='token symbol') - argparser.add_argument('--precision', type=str, help='token unit precision') - argparser.add_argument('directory', help='directory to initialize') + argparser.add_argument( + "--target", + action="append", + type=str, + default=[], + help="initialize network specification file with target", + ) + argparser.add_argument("--name", type=str, help="token name") + argparser.add_argument("--symbol", type=str, help="token symbol") + argparser.add_argument("--precision", type=str, help="token unit precision") + argparser.add_argument("directory", help="directory to initialize") def validate_args(args): @@ -25,11 +31,13 @@ def validate_args(args): def execute(config, eargs): - logg.info('initializing in {}'.format(eargs.directory)) + logg.info("initializing in {}".format(eargs.directory)) os.makedirs(eargs.directory) - ct = Token(eargs.directory, name=eargs.name, symbol=eargs.symbol, precision=eargs.precision) + ct = Token( + eargs.directory, name=eargs.name, symbol=eargs.symbol, precision=eargs.precision + ) cp = Proof(eargs.directory) cm = Meta(eargs.directory) ca = Attachment(eargs.directory) diff --git a/cic/cmd/show.py b/cic/cmd/show.py index b963706..3496c47 100644 --- a/cic/cmd/show.py +++ b/cic/cmd/show.py @@ -2,13 +2,20 @@ from cic.contract.components.proof import Proof from cic.contract.components.meta import Meta from cic.contract.components.attachment import Attachment -from cic.contract.components.network import Network +from cic.contract.network import Network from cic.contract.components.token import Token def process_args(argparser): - argparser.add_argument('-f', '--file', type=str, help='add file') - argparser.add_argument('-d', '--directory', type=str, dest='directory', default='.', help='cic data directory') + argparser.add_argument("-f", "--file", type=str, help="add file") + argparser.add_argument( + "-d", + "--directory", + type=str, + dest="directory", + default=".", + help="cic data directory", + ) def validate_args(args): @@ -28,8 +35,12 @@ def execute(config, eargs): ca.load() cn.load() - print("""[cic.header] -version = {}\n""".format(cp.version())) + print( + """[cic.header] +version = {}\n""".format( + cp.version() + ) + ) print("[cic.token]\n{}".format(ct)) print("[cic.proof]\n{}".format(cp)) print("[cic.meta]\n{}".format(cm)) diff --git a/cic/cmd/wizard.py b/cic/cmd/wizard.py index 4d6edfd..c4c6a45 100644 --- a/cic/cmd/wizard.py +++ b/cic/cmd/wizard.py @@ -16,7 +16,7 @@ from chainlib.chain import ChainSpec from cic.contract.components.proof import Proof from cic.contract.components.attachment import Attachment from cic.contract.components.meta import Meta -from cic.contract.components.network import Network +from cic.contract.network import Network from cic.contract.components.token import Token from cic.contract.contract import generate_contract, load_contract, deploy_contract @@ -61,7 +61,6 @@ def validate_args(_args): pass - def get_options(config: Config, eargs): # Defaults default_contract_registry = config.get( diff --git a/cic/contract/contract.py b/cic/contract/contract.py index cf08ab9..28fe3c9 100644 --- a/cic/contract/contract.py +++ b/cic/contract/contract.py @@ -11,11 +11,12 @@ from cic_types.ext.metadata import MetadataRequestsHandler from cic_types.ext.metadata.signer import Signer as MetadataSigner from chainlib.cli.config import Config from chainlib.chain import ChainSpec + # Local Modules from cic.contract.processor import ContractProcessor from cic.contract.components.attachment import Attachment from cic.contract.components.meta import Meta -from cic.contract.components.network import Network +from cic.contract.network import Network from cic.contract.components.proof import Proof from cic.contract.components.token import Token from cic.contract.helpers import init_writers_from_config @@ -87,7 +88,7 @@ def generate_contract( log.debug("Generating token") token = Token(directory, interactive=interactive) token.start() - + log.debug("Generating proof") proof = Proof(directory, interactive=interactive) proof.start() @@ -103,12 +104,14 @@ def generate_contract( network = Network(directory, targets=targets) network.start() - log.debug(f"""Populating infomation from network: + log.debug( + f"""Populating infomation from network: CIC_REGISTRY_ADDRESS: {config.get("CIC_REGISTRY_ADDRESS")} CHAIN_SPEC: {config.get("CHAIN_SPEC")} RPC_PROVIDER: {config.get("RPC_PROVIDER")} AUTH_KEY: {config.get("AUTH_KEY")} - """) + """ + ) for target in targets: m = importlib.import_module(f"cic.ext.{target}.start") m.extension_start( @@ -116,7 +119,9 @@ def generate_contract( registry_address=config.get("CIC_REGISTRY_ADDRESS"), chain_spec=ChainSpec.from_chain_str(config.get("CHAIN_SPEC")), rpc_provider=config.get("RPC_PROVIDER"), - key_account_address=config.get("AUTH_KEY"), # TODO this should come from the wallet keystore + key_account_address=config.get( + "AUTH_KEY" + ), # TODO this should come from the wallet keystore ) network.load() @@ -180,7 +185,7 @@ def deploy_contract( config.add(chain_spec, "CHAIN_SPEC", exists_ok=True) log.debug(f"using CHAIN_SPEC: {str(chain_spec)} from network") print(chain_spec) - + signer_hint = config.get("WALLET_KEY_FILE") (rpc, signer) = cmd_mod.parse_adapter(config, signer_hint) diff --git a/cic/contract/components/network.py b/cic/contract/network.py similarity index 99% rename from cic/contract/components/network.py rename to cic/contract/network.py index 650dfd3..e197434 100644 --- a/cic/contract/components/network.py +++ b/cic/contract/network.py @@ -1,11 +1,10 @@ # standard imports -import os import json import logging +import os # external imports from chainlib.chain import ChainSpec - # local imports from cic.contract.base import Data, data_dir -- 2.45.2 From ed7f3eeff5f541cb6887ea73a775c3718040e418 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 11:31:52 +0300 Subject: [PATCH 05/19] ci: lint fail under 8.00 --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 4d5bb3a..214d844 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,7 +15,7 @@ steps: # Install dependencies - pip install poetry - poetry install - - poetry run pylint cic + - poetry run pylint cic --fail-under=8.00 - poetry run pytest environment: LOGLEVEL: info -- 2.45.2 From b3ddf43285a659047b954f26e2889960b39aeb1b Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 12:09:22 +0300 Subject: [PATCH 06/19] ci: install extra requirements --- .drone.yml | 5 ++++- .gitignore | 2 +- README.md | 4 ++-- poetry.lock | 6 +++--- pyproject.toml | 4 +++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.drone.yml b/.drone.yml index 214d844..f3792ba 100644 --- a/.drone.yml +++ b/.drone.yml @@ -14,7 +14,7 @@ steps: commands: # Install dependencies - pip install poetry - - poetry install + - poetry install -E eth - poetry run pylint cic --fail-under=8.00 - poetry run pytest environment: @@ -25,6 +25,9 @@ steps: path: /root/.cache/pypoetry - name: pip_cache path: /root/.cache/pip + when: + event: + - push - name: publish image: python:3.8 commands: diff --git a/.gitignore b/.gitignore index 451613e..db4e97e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ __pycache__ *.pyc *.egg-info .venv -build +build/ .vscode .idea contracts diff --git a/README.md b/README.md index a86d19a..7b17e22 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ CIC-CLI provides tooling to generate and publish metadata in relation to token deployments. ```shell -pip install --extra-index-url https://pip.grassrootseconomics.net cic +pip install --extra-index-url https://pip.grassrootseconomics.net cic[eth] ``` ## Usage @@ -51,7 +51,7 @@ below are limited to the context of the EVM. ### Setup ``` - poetry install + poetry install -E eth ``` ### Running the CLI diff --git a/poetry.lock b/poetry.lock index eca897d..2a8fa48 100644 --- a/poetry.lock +++ b/poetry.lock @@ -363,7 +363,7 @@ reference = "pypi_" [[package]] name = "confini" -version = "0.5.5" +version = "0.5.7" description = "Parse, verify and merge all ini files in a single directory" category = "main" optional = false @@ -664,7 +664,7 @@ reference = "grassroots_" [[package]] name = "eth-token-index" -version = "0.2.4.linux-x86_64" +version = "0.2.4" description = "Token symbol to address unique index" category = "main" optional = true @@ -1956,7 +1956,7 @@ eth = ["chainlib-eth", "eth-token-index", "eth-address-index", "okota", "cic_eth [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "2f7c01af1a14bcfeedf238b5ac7135e304a9fe162d27ee0638d5551467979be6" +content-hash = "aeaba6fb1c3ad6d40f16cc9302ecac21fd7dbb50af085dc108c7ada2cf308067" [metadata.files] asn1crypto = [] diff --git a/pyproject.toml b/pyproject.toml index 8b63011..beb278b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,10 +32,12 @@ cic = 'cic.runnable.cic_cmd:main' [[tool.poetry.source]] name = "grassroots_" url = "https://pip.grassrootseconomics.net/" +default = true [[tool.poetry.source]] name = "pypi_" url = "https://pypi.org/simple/" +secondary = true [tool.poetry.dependencies] python = "^3.8" @@ -46,7 +48,7 @@ chainlib = "~0.0.17" cbor2 = "5.4.1" chainlib-eth = { version = "~0.0.25", optional = true } -eth-token-index = { version = "~0.2.4", optional = true } +eth-token-index = { version = "0.2.4", optional = true } eth-address-index = { version = "~0.2.4", optional = true } okota = { version = "~0.2.5", optional = true } cic_eth_registry = { version = "~0.6.6", optional = true } -- 2.45.2 From 48ee8050c17edb21b0dc4065bf0018b1502d4a8c Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 12:30:19 +0300 Subject: [PATCH 07/19] fix: add missing json import --- cic/contract/network.py | 2 +- cic/writers.py | 35 +++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/cic/contract/network.py b/cic/contract/network.py index e197434..2dc774b 100644 --- a/cic/contract/network.py +++ b/cic/contract/network.py @@ -134,7 +134,7 @@ class Network(Data): for resource in self.resources.keys(): for content_key in self.resources[resource]['contents'].keys(): content_value = self.resources[resource]['contents'][content_key] - if content_value == None: + if content_value is None: content_value = '' s += f'{resource}.{content_key} = {content_value}\n' diff --git a/cic/writers.py b/cic/writers.py index 3a139e3..8b2e9ca 100644 --- a/cic/writers.py +++ b/cic/writers.py @@ -4,10 +4,9 @@ import logging import os import sys import urllib.request -from typing import TYPE_CHECKING - -from cic_types.ext.metadata import MetadataRequestsHandler +import json +from cic_types.ext.metadata import MetadataRequestsHandler, MetadataPointer logg = logging.getLogger(__name__) @@ -22,7 +21,7 @@ class OutputWriter: class StdoutWriter(OutputWriter): def write(self, k, v): - sys.stdout.write("{}\t{}\n".format(k, v)) + sys.stdout.write(f"{k}\t{v}\n") class KVWriter(OutputWriter): @@ -35,7 +34,7 @@ class KVWriter(OutputWriter): def write(self, k, v): fp = os.path.join(self.path, str(k)) - logg.debug("path write {} {}".format(fp, str(v))) + logg.debug(f"path write {fp} {str(v)}") f = open(fp, "wb") f.write(v) f.close() @@ -48,12 +47,12 @@ class HTTPWriter(OutputWriter): def write(self, k, v): path = self.path - if k != None: + if k is not None: path = os.path.join(path, k) logg.debug(f"http writer post {path} \n key: {k}, value: {v}") rq = urllib.request.Request(path, method="POST", data=v) r = urllib.request.urlopen(rq) - logg.info("http writer submitted at {}".format(r.read())) + logg.info(f"http writer submitted at {r.read()}") class KeyedWriter(OutputWriter): @@ -61,14 +60,14 @@ class KeyedWriter(OutputWriter): self.writer_keyed = writer_keyed self.writer_immutable = writer_immutable - def write(self, key, value): - logg.debug(f"writing keywriter key: {key} value: {value}") + def write(self, k, v): + logg.debug(f"writing keywriter key: {k} value: {v}") if isinstance(value, str): value = value.encode("utf-8") - if self.writer_keyed != None: - self.writer_keyed.write(key, value) - if self.writer_immutable != None: - self.writer_immutable.write(None, value) + if self.writer_keyed is not None: + self.writer_keyed.write(k, v) + if self.writer_immutable is not None: + self.writer_immutable.write(None, v) class KeyedWriterFactory: @@ -78,16 +77,16 @@ class KeyedWriterFactory: self.key_writer_constructor = key_writer_constructor self.immutable_writer_constructor = immutable_writer_constructor self.x = {} - for k in kwargs.keys(): - logg.debug("adding key {} t keyed writer factory".format(k)) - self.x[k] = kwargs[k] + for k, v in kwargs.items(): + logg.debug(f"adding key {k} t keyed writer factory") + self.x[k] = v def new(self, path=None, *args, **kwargs): writer_keyed = None writer_immutable = None - if self.key_writer_constructor != None: + if self.key_writer_constructor is not None: writer_keyed = self.key_writer_constructor(path, **self.x) - if self.immutable_writer_constructor != None: + if self.immutable_writer_constructor is not None: writer_immutable = self.immutable_writer_constructor(path, **self.x) return KeyedWriter(writer_keyed, writer_immutable) -- 2.45.2 From 40e386db1175839394f2480a1a3e1bbfc52edea9 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 12:40:30 +0300 Subject: [PATCH 08/19] fix: change name to cic-cli --- .drone.yml | 2 ++ .gitignore | 3 ++- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index f3792ba..69af6a5 100644 --- a/.drone.yml +++ b/.drone.yml @@ -40,6 +40,8 @@ steps: when: branch: - master + event: + - push environment: LOGLEVEL: info GIT_SSL_NO_VERIFY: 1 diff --git a/.gitignore b/.gitignore index db4e97e..5ad773c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ build/ .idea contracts *.egg -.coverage \ No newline at end of file +.coverage +dist/ \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index beb278b..f5b666a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "cic" +name = "cic-cli" version = "0.0.2" description = "Generic cli tooling for the CIC token network" authors = [ -- 2.45.2 From 7ccdde481b54fb7ec07c45023ababf6651e88e07 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 12:43:56 +0300 Subject: [PATCH 09/19] chore: cleanup default config --- cic/data/config/config.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cic/data/config/config.ini b/cic/data/config/config.ini index 4311461..448b7f9 100644 --- a/cic/data/config/config.ini +++ b/cic/data/config/config.ini @@ -8,13 +8,13 @@ ext_writer = cic.writers.KVWriter registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 [meta] -url = http://localhost:63380 +url = https://meta.grassecon.net http_origin = [auth] type = gnupg -db_path = /home/will/.local/share/cic/clicada -keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc -key = CCE2E1D2D0E36ADE0405E2D0995BB21816313BD5 -passphrase = merman +db_path = +keyfile_path = +key = +passphrase = -- 2.45.2 From 1d4b0512ad65b4d2903bd7d022e562cda158a592 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 13:48:58 +0300 Subject: [PATCH 10/19] fix(ext): allow loading chain_spec from config --- cic/cmd/ext.py | 8 ++++---- cic/ext/eth/start.py | 32 ++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/cic/cmd/ext.py b/cic/cmd/ext.py index d015a0e..78b0c11 100644 --- a/cic/cmd/ext.py +++ b/cic/cmd/ext.py @@ -10,7 +10,7 @@ from cic.contract.network import Network def process_args(argparser): argparser.add_argument( - "--registry", required=True, type=str, help="contract registry address" + "--registry", type=str, help="contract registry address" ) argparser.add_argument( "-d", "--directory", type=str, dest="directory", default=".", help="directory" @@ -28,11 +28,11 @@ def execute(config, eargs): cn = Network(eargs.directory, targets=eargs.target) cn.load() - chain_spec = ChainSpec.from_chain_str(eargs.i) + chain_spec = ChainSpec.from_chain_str(eargs.i or config.get("CHAIN_SPEC")) m = importlib.import_module(f"cic.ext.{eargs.target}.start") m.extension_start( cn, - registry_address=eargs.registry, + registry_address=eargs.registry or config.get("CIC_REGISTRY_ADDRESS"), chain_spec=chain_spec, rpc_provider=config.get("RPC_PROVIDER"), - ) + ) # TODO add key account address diff --git a/cic/ext/eth/start.py b/cic/ext/eth/start.py index 1482c74..7f6e6ce 100644 --- a/cic/ext/eth/start.py +++ b/cic/ext/eth/start.py @@ -1,6 +1,6 @@ # external imports -from cic_eth_registry import CICRegistry from chainlib.eth.connection import RPCConnection +from cic_eth_registry import CICRegistry def extension_start(network, *args, **kwargs): @@ -9,22 +9,26 @@ def extension_start(network, *args, **kwargs): :param network: Network object to read and write settings from :type network: cic.network.Network """ - CICRegistry.address = kwargs['registry_address'] - key_account_address = kwargs['key_account_address'] or '' + CICRegistry.address = kwargs.get("registry_address") + key_account_address = kwargs.get("key_account_address") + RPCConnection.register_location( + kwargs.get("rpc_provider"), kwargs.get("chain_spec") + ) + conn = RPCConnection.connect(kwargs.get("chain_spec")) - RPCConnection.register_location(kwargs['rpc_provider'], kwargs['chain_spec']) - conn = RPCConnection.connect(kwargs['chain_spec']) + registry = CICRegistry(kwargs.get("chain_spec"), conn) - registry = CICRegistry(kwargs['chain_spec'], conn) + address_declarator = registry.by_name("AddressDeclarator") + network.resource_set( + "eth", "address_declarator", address_declarator, key_account=key_account_address + ) - address_declarator = registry.by_name('AddressDeclarator') - network.resource_set('eth', 'address_declarator', address_declarator, key_account=key_account_address) + token_index = registry.by_name("TokenRegistry") + network.resource_set( + "eth", "token_index", token_index, key_account=key_account_address + ) - token_index = registry.by_name('TokenRegistry') - network.resource_set('eth', 'token_index', token_index, key_account=key_account_address) + network.resource_set("eth", "token", None, key_account=key_account_address) - network.resource_set('eth', 'token', None, key_account=key_account_address) - - - network.set('eth', kwargs['chain_spec']) + network.set("eth", kwargs["chain_spec"]) network.save() -- 2.45.2 From 556366a93384bba51aa617d54bcf50f4473b790a Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 16:36:58 +0300 Subject: [PATCH 11/19] feat(wizard): add ability to select wallet address --- README.md | 3 +++ cic/contract/contract.py | 21 +++++++++++++++++---- cic/ext/eth/__init__.py | 2 +- cic/ext/eth/rpc.py | 14 ++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7b17e22..1e22c02 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,9 @@ below are limited to the context of the EVM. poetry run cic -h ``` +```bash + poetry run cic wizard ./somewhere -c ./config/dev-docker +``` ### Tests ``` diff --git a/cic/contract/contract.py b/cic/contract/contract.py index 28fe3c9..5062b55 100644 --- a/cic/contract/contract.py +++ b/cic/contract/contract.py @@ -109,19 +109,32 @@ def generate_contract( CIC_REGISTRY_ADDRESS: {config.get("CIC_REGISTRY_ADDRESS")} CHAIN_SPEC: {config.get("CHAIN_SPEC")} RPC_PROVIDER: {config.get("RPC_PROVIDER")} - AUTH_KEY: {config.get("AUTH_KEY")} """ ) for target in targets: + # TODO Clean this up + modname = f"cic.ext.{target}" + cmd_mod = importlib.import_module(modname) + signer_hint = config.get("WALLET_KEY_FILE") + keys = cmd_mod.list_keys(config, signer_hint) + for idx, key in enumerate(keys): + print(f"{idx} - {key} ") + selecting_key = True + while selecting_key: + idx = int(input("Select key: ")) + if keys[idx] is not None: + key_account_address = keys[idx] + selecting_key = False + else: + print("Invalid key, try again") + m = importlib.import_module(f"cic.ext.{target}.start") m.extension_start( network, registry_address=config.get("CIC_REGISTRY_ADDRESS"), chain_spec=ChainSpec.from_chain_str(config.get("CHAIN_SPEC")), rpc_provider=config.get("RPC_PROVIDER"), - key_account_address=config.get( - "AUTH_KEY" - ), # TODO this should come from the wallet keystore + key_account_address=key_account_address ) network.load() diff --git a/cic/ext/eth/__init__.py b/cic/ext/eth/__init__.py index edcd875..61ae93b 100644 --- a/cic/ext/eth/__init__.py +++ b/cic/ext/eth/__init__.py @@ -18,7 +18,7 @@ from giftable_erc20_token import GiftableToken from hexathon import add_0x, strip_0x # local imports -from cic.ext.eth.rpc import parse_adapter +from cic.ext.eth.rpc import parse_adapter, list_keys from cic.extension import Extension diff --git a/cic/ext/eth/rpc.py b/cic/ext/eth/rpc.py index 19b4ca5..783915b 100644 --- a/cic/ext/eth/rpc.py +++ b/cic/ext/eth/rpc.py @@ -52,3 +52,17 @@ def parse_adapter(config, signer_hint): rpc.connect_by_config(config) return (rpc.conn, signer) + +# TODO Find a better place for this +def list_keys(config, signer_hint): + keystore = None + if signer_hint is None: + logg.info("signer hint missing") + return None + st = os.stat(signer_hint) + if stat.S_ISDIR(st.st_mode): + logg.debug("signer hint is directory") + keystore = EthKeystoreDirectory() + keystore.process_dir(signer_hint, default_password=config.get('WALLET_PASSPHRASE', '')) + + return keystore.list() \ No newline at end of file -- 2.45.2 From 41dbd5a400287d4687d0830017466b9a43054ecf Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 16:37:25 +0300 Subject: [PATCH 12/19] fix: incorrect var name --- cic/writers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cic/writers.py b/cic/writers.py index 8b2e9ca..ce363d5 100644 --- a/cic/writers.py +++ b/cic/writers.py @@ -62,8 +62,8 @@ class KeyedWriter(OutputWriter): def write(self, k, v): logg.debug(f"writing keywriter key: {k} value: {v}") - if isinstance(value, str): - value = value.encode("utf-8") + if isinstance(v, str): + v = v.encode("utf-8") if self.writer_keyed is not None: self.writer_keyed.write(k, v) if self.writer_immutable is not None: -- 2.45.2 From 525aab8c771461e3fc4fab556f89cc8834527e68 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 16:38:17 +0300 Subject: [PATCH 13/19] chore: clean up --- cic/cmd/wizard.py | 80 +++--------------------------------- cic/data/config/config.ini | 2 - config/dev-docker/config.ini | 4 +- config/prod/config.ini | 2 - 4 files changed, 6 insertions(+), 82 deletions(-) diff --git a/cic/cmd/wizard.py b/cic/cmd/wizard.py index c4c6a45..3b6f6b7 100644 --- a/cic/cmd/wizard.py +++ b/cic/cmd/wizard.py @@ -1,27 +1,12 @@ from __future__ import annotations # standard import -import importlib -import json import logging -import os -from typing import TYPE_CHECKING, List -import requests - -# external imports -from chainlib.chain import ChainSpec +from chainlib.cli.config import Config # local imports -from cic.contract.components.proof import Proof -from cic.contract.components.attachment import Attachment -from cic.contract.components.meta import Meta -from cic.contract.network import Network -from cic.contract.components.token import Token -from cic.contract.contract import generate_contract, load_contract, deploy_contract - -if TYPE_CHECKING: - from chainlib.cli.config import Config +from cic.contract.contract import deploy_contract, generate_contract, load_contract log = logging.getLogger(__name__) @@ -61,72 +46,17 @@ def validate_args(_args): pass -def get_options(config: Config, eargs): - # Defaults - default_contract_registry = config.get( - "CIC_REGISTRY_ADDRESS" - ) # Comes from /home/will/grassroots/cic-staff-installer/var/cic-staff-client/CIC_REGISTRY_ADDRESS - default_key_account = config.get("AUTH_KEY") - # https://meta.grassrootseconomics.net - # https://auth.grassrootseconomics.net Authenticated Meta - - default_metadata_endpoint = config.get("META_URL") - # Keyring folder needs to be dumped out as a private key file from $HOME/.config/cic/staff-client/.gnupg - default_wallet_keyfile = eargs.y or config.get( - "WALLET_KEY_FILE" - ) # Show possible wallet keys - - # Should be an input??? - default_wallet_passphrase = config.get("WALLET_PASSPHRASE", "merman") - default_chain_spec = config.get("CHAIN_SPEC") - default_rpc_provider = config.get("RPC_PROVIDER") - - contract_registry = ( - input(f"Enter Contract Registry ({default_contract_registry}): ") - or default_contract_registry - ) - rpc_provider = ( - input(f"Enter RPC Provider ({default_rpc_provider}): ") or default_rpc_provider - ) - chain_spec = ChainSpec.from_chain_str( - (input(f"Enter ChainSpec ({default_chain_spec}): ") or default_chain_spec) - ) - key_account = ( - input(f"Enter KeyAccount ({default_key_account}): ") or default_key_account - ) - metadata_endpoint = ( - input(f"Enter Metadata Endpoint ({default_metadata_endpoint}): ") - or default_metadata_endpoint - ) - auth_passphrase = config.get("AUTH_PASSPHRASE") - auth_keyfile_path = config.get("AUTH_KEYFILE_PATH") - auth_db_path = config.get("AUTH_DB_PATH") - - options = [ - auth_db_path, - auth_keyfile_path, - auth_passphrase, - contract_registry, - key_account, - chain_spec, - rpc_provider, - metadata_endpoint, - default_wallet_keyfile, - default_wallet_passphrase, - ] - print(options) - return options - - ExtraArgs = {"skip_gen": str, "skip_deploy": str, "target": str, "path": str, "p": str} -def execute(config, eargs: ExtraArgs): +def execute(config: Config, eargs: ExtraArgs): print(f"eargs: {eargs}") directory = eargs.path target = eargs.target skip_gen = eargs.skip_gen skip_deploy = eargs.skip_deploy + wallet_keystore = eargs.y + config.add(wallet_keystore, "WALLET_KEY_FILE", exists_ok=True) if not skip_gen: contract = generate_contract(directory, [target], config, interactive=True) diff --git a/cic/data/config/config.ini b/cic/data/config/config.ini index 448b7f9..581f951 100644 --- a/cic/data/config/config.ini +++ b/cic/data/config/config.ini @@ -13,8 +13,6 @@ http_origin = [auth] type = gnupg -db_path = keyfile_path = -key = passphrase = diff --git a/config/dev-docker/config.ini b/config/dev-docker/config.ini index a1e19ca..3366663 100644 --- a/config/dev-docker/config.ini +++ b/config/dev-docker/config.ini @@ -16,14 +16,12 @@ provider = http://localhost:63545 [auth] type = gnupg -db_path = /home/will/.local/share/cic/clicada -key = eb3907ecad74a0013c259d5874ae7f22dcbcc95c keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc passphrase = merman [wallet] key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore -passphrase = +passphrase = [chain] spec = evm:byzantium:8996:bloxberg \ No newline at end of file diff --git a/config/prod/config.ini b/config/prod/config.ini index cd27599..ce1aa8f 100644 --- a/config/prod/config.ini +++ b/config/prod/config.ini @@ -16,8 +16,6 @@ provider = https://rpc.grassecon.net [auth] type = gnupg -db_path = /home/will/.local/share/cic/clicada -key = CCE2E1D2D0E36ADE0405E2D0995BB21816313BD5 keyfile_path = /home/will/.config/cic/staff-client/user.asc passphrase = queenmarlena -- 2.45.2 From 9ae198f0f6fd258e712acdabff26d7c12ff7b4f8 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 1 Mar 2022 17:06:01 +0300 Subject: [PATCH 14/19] chore: download contracts to tmp dir --- cic/cmd/wizard.py | 3 ++- cic/contract/helpers.py | 24 ++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/cic/cmd/wizard.py b/cic/cmd/wizard.py index 3b6f6b7..3668c9f 100644 --- a/cic/cmd/wizard.py +++ b/cic/cmd/wizard.py @@ -56,7 +56,8 @@ def execute(config: Config, eargs: ExtraArgs): skip_gen = eargs.skip_gen skip_deploy = eargs.skip_deploy wallet_keystore = eargs.y - config.add(wallet_keystore, "WALLET_KEY_FILE", exists_ok=True) + if wallet_keystore: + config.add(wallet_keystore, "WALLET_KEY_FILE", exists_ok=True) if not skip_gen: contract = generate_contract(directory, [target], config, interactive=True) diff --git a/cic/contract/helpers.py b/cic/contract/helpers.py index 3ec71b5..93c41c5 100644 --- a/cic/contract/helpers.py +++ b/cic/contract/helpers.py @@ -5,6 +5,8 @@ import sys import json import requests import importlib +import tempfile +import hashlib # local imports from cic.writers import OutputWriter @@ -21,17 +23,24 @@ CONTRACTS = [ "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", "name": "Demurrage Token Single No Cap", }, + { + "url":"https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/lash/gas-safety-valve/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", + "name": "Demurrage Token Single No Cap (gas-safety-valve)", + } ] # Download File from Url -def download_file(url: str, directory: str, filename=None) -> (str, bytes): - os.makedirs(directory, exist_ok=True) +def download_file(url: str, filename=None) -> (str, bytes): + directory = tempfile.gettempdir() filename = filename if filename else url.split("/")[-1] - path = os.path.join(directory, filename) + hash_object = hashlib.md5(url.encode()) + path = os.path.join(directory, hash_object.hexdigest()) + log.debug(f"Downloading {filename} to {path}") if not os.path.exists(path): log.debug(f"Downloading {filename}") r = requests.get(url, allow_redirects=True) - open(path, "wb").write(r.content) + with open(path, "wb") as f: + f.write(r.content) return path return path @@ -50,15 +59,14 @@ def select_contract(): val = input("Select contract (C,0,1..): ") if val.isdigit() and int(val) < len(CONTRACTS): contract = CONTRACTS[int(val)] - directory = f"./contracts/{contract['name']}" - bin_path = os.path.abspath(download_file(contract["url"] + ".bin", directory)) - json_path = download_file(contract["url"] + ".json", directory) + bin_path = os.path.abspath(download_file(contract["url"] + ".bin")) + json_path = download_file(contract["url"] + ".json") elif val == "C": possible_bin_location = input("Enter a path or url to a contract.bin: ") if possible_bin_location.startswith('http'): # possible_bin_location is url - bin_path = download_file(possible_bin_location, directory) + bin_path = download_file(possible_bin_location) else: # possible_bin_location is path if os.path.exists(possible_bin_location): -- 2.45.2 From e731a2c32f938e511cd6c70ff9c39a3f106c5154 Mon Sep 17 00:00:00 2001 From: William Luke Date: Thu, 3 Mar 2022 10:44:12 +0300 Subject: [PATCH 15/19] chore: remove gas-safety-valve test --- cic/contract/helpers.py | 20 +++++++------------- config/prod/config.ini | 4 ++-- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cic/contract/helpers.py b/cic/contract/helpers.py index 93c41c5..c5b855d 100644 --- a/cic/contract/helpers.py +++ b/cic/contract/helpers.py @@ -22,10 +22,6 @@ CONTRACTS = [ { "url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", "name": "Demurrage Token Single No Cap", - }, - { - "url":"https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/lash/gas-safety-valve/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap", - "name": "Demurrage Token Single No Cap (gas-safety-valve)", } ] @@ -33,15 +29,13 @@ CONTRACTS = [ def download_file(url: str, filename=None) -> (str, bytes): directory = tempfile.gettempdir() filename = filename if filename else url.split("/")[-1] - hash_object = hashlib.md5(url.encode()) - path = os.path.join(directory, hash_object.hexdigest()) - log.debug(f"Downloading {filename} to {path}") - if not os.path.exists(path): - log.debug(f"Downloading {filename}") - r = requests.get(url, allow_redirects=True) - with open(path, "wb") as f: - f.write(r.content) - return path + log.debug(f"Downloading {filename}") + r = requests.get(url, allow_redirects=True) + content_hash = hashlib.md5(r.content).hexdigest() + path = os.path.join(directory, content_hash) + with open(path, "wb") as f: + f.write(r.content) + log.debug(f"{filename} downloaded to {path}") return path def get_contract_args(data: list): diff --git a/config/prod/config.ini b/config/prod/config.ini index ce1aa8f..4bc766c 100644 --- a/config/prod/config.ini +++ b/config/prod/config.ini @@ -5,7 +5,7 @@ proof_writer = cic.writers.KVWriter ext_writer = cic.writers.KVWriter [cic] -registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 +registry_address = 0x86c616fd2f020289d6fd7a98f3bd7539b335c995 [meta] url = https://meta.grassecon.net @@ -24,4 +24,4 @@ key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migratio passphrase = [chain] -spec = evm:byzantium:5050:bloxberg \ No newline at end of file +spec = evm:kitabu:5050:sarafu \ No newline at end of file -- 2.45.2 From 1a769205ea9b0a0c1887d51b09ed07dcc46c8b88 Mon Sep 17 00:00:00 2001 From: William Luke Date: Thu, 3 Mar 2022 10:49:35 +0300 Subject: [PATCH 16/19] chore: remove hardcoded auth vars --- cic/cmd/export.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cic/cmd/export.py b/cic/cmd/export.py index 5199967..bfaa6b0 100644 --- a/cic/cmd/export.py +++ b/cic/cmd/export.py @@ -71,8 +71,6 @@ def init_writers_from_config(config): ExtraArgs = { "target": str, - "key_file_path": str, - "gpg_passphrase": str, "directory": str, "output_directory": str, "metadata_endpoint": Optional[str], @@ -81,9 +79,6 @@ ExtraArgs = { def execute(config, eargs: ExtraArgs): - # !TODO Remove this - eargs.key_file_path = "/home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc" - eargs.gpg_passphrase = "merman" modname = f"cic.ext.{eargs.target}" cmd_mod = importlib.import_module(modname) @@ -93,8 +88,8 @@ def execute(config, eargs: ExtraArgs): if eargs.metadata_endpoint != None: MetadataRequestsHandler.base_url = eargs.metadata_endpoint MetadataSigner.gpg_path = os.path.join("/tmp") - MetadataSigner.key_file_path = eargs.key_file_path - MetadataSigner.gpg_passphrase = eargs.gpg_passphrase + MetadataSigner.key_file_path = config.get("AUTH_KEYFILE_PATH") + MetadataSigner.gpg_passphrase = config.get("AUTH_PASSPHRASE") writers["proof"] = KeyedWriterFactory(MetadataWriter, HTTPWriter).new writers["attachment"] = KeyedWriterFactory(None, HTTPWriter).new writers["meta"] = MetadataWriter -- 2.45.2 From 0dc25002f69576f2ec95e28068a1efa84c649b2a Mon Sep 17 00:00:00 2001 From: William Luke Date: Mon, 7 Mar 2022 12:25:45 +0300 Subject: [PATCH 17/19] chore: clean up --- cic/cmd/wizard.py | 9 +++-- cic/contract/contract.py | 71 +++++++++++++++++++++------------------- cic/contract/network.py | 4 ++- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/cic/cmd/wizard.py b/cic/cmd/wizard.py index 3668c9f..34bd125 100644 --- a/cic/cmd/wizard.py +++ b/cic/cmd/wizard.py @@ -50,7 +50,6 @@ ExtraArgs = {"skip_gen": str, "skip_deploy": str, "target": str, "path": str, "p def execute(config: Config, eargs: ExtraArgs): - print(f"eargs: {eargs}") directory = eargs.path target = eargs.target skip_gen = eargs.skip_gen @@ -66,8 +65,12 @@ def execute(config: Config, eargs: ExtraArgs): print(contract) + print(f"Meta: {config.get('META_URL')}") + print(f"ChainSpec: {config.get('CHAIN_SPEC', contract.network.chain_spec(target))}") + print(f"RPC: {config.get('RPC_PROVIDER')}\n") + if not skip_deploy: - ready_to_deploy = input("Ready to deploy? (y/n): ") + ready_to_deploy = input("Are you ready to Deploy? (y/n): ") if ready_to_deploy == "y": deploy_contract( config=config, @@ -76,7 +79,7 @@ def execute(config: Config, eargs: ExtraArgs): ) print("Deployed") else: - print("Not deploying") + print("Skipping deployment") if __name__ == "__main__": diff --git a/cic/contract/contract.py b/cic/contract/contract.py index 5062b55..cf54fa7 100644 --- a/cic/contract/contract.py +++ b/cic/contract/contract.py @@ -3,25 +3,25 @@ import importlib import json import logging import os -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING, List + import requests +from chainlib.chain import ChainSpec +from chainlib.cli.config import Config +from cic.contract.components.attachment import Attachment +from cic.contract.components.meta import Meta +from cic.contract.components.proof import Proof +from cic.contract.components.token import Token +from cic.contract.helpers import init_writers_from_config +from cic.contract.network import Network + +# Local Modules +from cic.contract.processor import ContractProcessor +from cic.writers import HTTPWriter, KeyedWriterFactory, MetadataWriter, OutputWriter # external imports from cic_types.ext.metadata import MetadataRequestsHandler from cic_types.ext.metadata.signer import Signer as MetadataSigner -from chainlib.cli.config import Config -from chainlib.chain import ChainSpec - -# Local Modules -from cic.contract.processor import ContractProcessor -from cic.contract.components.attachment import Attachment -from cic.contract.components.meta import Meta -from cic.contract.network import Network -from cic.contract.components.proof import Proof -from cic.contract.components.token import Token -from cic.contract.helpers import init_writers_from_config -from cic.writers import HTTPWriter, KeyedWriterFactory, OutputWriter, MetadataWriter - log = logging.getLogger(__name__) @@ -45,12 +45,12 @@ class Contract: def __str__(self): s = "" - s += f"[cic.header]\nversion = {self.proof.version()}\n" - s += f"[cic.token]\n{self.token}" - s += f"[cic.proof]\n{self.proof}" - s += f"[cic.meta]\n{self.meta}" - s += f"[cic.attachment]\n{self.attachment}" - s += f"[cic.network]\n{self.network}" + s += f"\n[cic.header]\nversion = {self.proof.version()}\n\n" + s += f"[cic.token]\n{self.token}\n" + s += f"[cic.proof]\n{self.proof}\n" + s += f"[cic.meta]\n{self.meta}\n" + s += f"[cic.attachment]\n{self.attachment}\n" + s += f"[cic.network]\n{self.network}\n" return s @@ -76,10 +76,10 @@ def generate_contract( ) -> Contract: if os.path.exists(directory): contine = input( - "Directory already exists, Would you like to delete it? (y/n): " + f"Directory {directory} already exists, Would you like to delete it? (y/n): " ) if contine.lower() != "y": - print("Trying to load existing contract") + log.debug("Trying to load existing contract") return load_contract(directory) else: print(f"Deleted {directory}") @@ -117,16 +117,20 @@ def generate_contract( cmd_mod = importlib.import_module(modname) signer_hint = config.get("WALLET_KEY_FILE") keys = cmd_mod.list_keys(config, signer_hint) - for idx, key in enumerate(keys): - print(f"{idx} - {key} ") - selecting_key = True - while selecting_key: - idx = int(input("Select key: ")) - if keys[idx] is not None: - key_account_address = keys[idx] - selecting_key = False - else: - print("Invalid key, try again") + if len(keys) > 1: + print(f"More than one key found, please select one:") + for idx, key in enumerate(keys): + print(f"{idx} - {key} ") + selecting_key = True + while selecting_key: + idx = int(input("Select key: ")) + if keys[idx] is not None: + key_account_address = keys[idx] + selecting_key = False + else: + print("Invalid key, try again") + else: + key_account_address = keys[0] m = importlib.import_module(f"cic.ext.{target}.start") m.extension_start( @@ -134,7 +138,7 @@ def generate_contract( registry_address=config.get("CIC_REGISTRY_ADDRESS"), chain_spec=ChainSpec.from_chain_str(config.get("CHAIN_SPEC")), rpc_provider=config.get("RPC_PROVIDER"), - key_account_address=key_account_address + key_account_address=key_account_address, ) network.load() @@ -197,7 +201,6 @@ def deploy_contract( chain_spec = cn.chain_spec config.add(chain_spec, "CHAIN_SPEC", exists_ok=True) log.debug(f"using CHAIN_SPEC: {str(chain_spec)} from network") - print(chain_spec) signer_hint = config.get("WALLET_KEY_FILE") (rpc, signer) = cmd_mod.parse_adapter(config, signer_hint) diff --git a/cic/contract/network.py b/cic/contract/network.py index 2dc774b..71b71ea 100644 --- a/cic/contract/network.py +++ b/cic/contract/network.py @@ -132,10 +132,12 @@ class Network(Data): def __str__(self): s = '' for resource in self.resources.keys(): + chainspec = ChainSpec.from_dict(self.resources[resource]['chain_spec']) + s += f'{resource}.chain_spec: {str(chainspec)}\n' for content_key in self.resources[resource]['contents'].keys(): content_value = self.resources[resource]['contents'][content_key] if content_value is None: content_value = '' - s += f'{resource}.{content_key} = {content_value}\n' + s += f'{resource}.contents.{content_key} = {json.dumps(content_value, indent=4, sort_keys=True)}\n' return s -- 2.45.2 From 67f947a9af16dc01fb68459a51629320264d281f Mon Sep 17 00:00:00 2001 From: William Luke Date: Mon, 7 Mar 2022 12:26:53 +0300 Subject: [PATCH 18/19] docs: rename prod config to testnet --- README.md | 4 ++-- config/{prod => test-net}/config.ini | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename config/{prod => test-net}/config.ini (100%) diff --git a/README.md b/README.md index 1e22c02..cb0b7b5 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ pip install --extra-index-url https://pip.grassrootseconomics.net cic[eth] # Local cic wizard ./somewhere -c ./config/dev-docker -# Production -cic wizard ./somewhere -c ./config/prod +# Test Net +cic wizard ./somewhere -c ./config/test-net ``` ### Modular diff --git a/config/prod/config.ini b/config/test-net/config.ini similarity index 100% rename from config/prod/config.ini rename to config/test-net/config.ini -- 2.45.2 From 47a9b259ae54c34df9af4aa1fb176070d305296a Mon Sep 17 00:00:00 2001 From: William Luke Date: Mon, 14 Mar 2022 15:48:36 +0300 Subject: [PATCH 19/19] fix: add getpass --- .gitignore | 1 + README.md | 12 ++++++++--- cic/ext/eth/rpc.py | 17 +++++++++------- config/docker/config.ini | 27 +++++++++++++++++++++++++ config/{dev-docker => local}/config.ini | 4 ++-- config/{test-net => mainnet}/config.ini | 4 ++-- config/testnet/config.ini | 27 +++++++++++++++++++++++++ 7 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 config/docker/config.ini rename config/{dev-docker => local}/config.ini (90%) rename config/{test-net => mainnet}/config.ini (82%) create mode 100644 config/testnet/config.ini diff --git a/.gitignore b/.gitignore index 5ad773c..231f580 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ build/ contracts *.egg .coverage +deployments/ dist/ \ No newline at end of file diff --git a/README.md b/README.md index cb0b7b5..ec4eff0 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,20 @@ token deployments. ```shell pip install --extra-index-url https://pip.grassrootseconomics.net cic[eth] ``` +## Setup +### Importing a wallet from metamask +- Export the accounts private key [Instructions](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key) +- Save the private key to a file +- Run `eth-keyfile -k > ~/.config/cic/keystore/keyfile.json` ## Usage ### Using the wizard ``` # Local -cic wizard ./somewhere -c ./config/dev-docker +cic wizard ./somewhere -c ./config/docker # Test Net -cic wizard ./somewhere -c ./config/test-net +cic wizard ./somewhere -c ./config/testnet ``` ### Modular @@ -61,8 +66,9 @@ below are limited to the context of the EVM. ``` ```bash - poetry run cic wizard ./somewhere -c ./config/dev-docker + poetry run cic wizard ./somewhere -c ./config/docker ``` + ### Tests ``` diff --git a/cic/ext/eth/rpc.py b/cic/ext/eth/rpc.py index 783915b..3b7bfbc 100644 --- a/cic/ext/eth/rpc.py +++ b/cic/ext/eth/rpc.py @@ -1,17 +1,19 @@ # standard imports -import stat -import os +from getpass import getpass import logging +import os +import stat # external imports from funga.eth.keystore.dict import DictKeystore from funga.eth.signer import EIP155Signer -from chainlib.eth.cli import Rpc from chainlib.cli import Wallet +from chainlib.eth.cli import Rpc # local imports from cic.keystore import KeystoreDirectory + logg = logging.getLogger(__name__) @@ -22,7 +24,8 @@ class EthKeystoreDirectory(DictKeystore, KeystoreDirectory): """ - +def get_passphrase(): + return getpass('Enter passphrase: ') def parse_adapter(config, signer_hint): """Determine and instantiate signer and rpc from configuration. @@ -44,7 +47,7 @@ def parse_adapter(config, signer_hint): if stat.S_ISDIR(st.st_mode): logg.debug("signer hint is directory") keystore = EthKeystoreDirectory() - keystore.process_dir(signer_hint) + keystore.process_dir(signer_hint, password_retriever=get_passphrase) w = Wallet(EIP155Signer, keystore=keystore) signer = EIP155Signer(keystore) @@ -63,6 +66,6 @@ def list_keys(config, signer_hint): if stat.S_ISDIR(st.st_mode): logg.debug("signer hint is directory") keystore = EthKeystoreDirectory() - keystore.process_dir(signer_hint, default_password=config.get('WALLET_PASSPHRASE', '')) + keystore.process_dir(signer_hint, default_password=config.get('WALLET_PASSPHRASE', ''), password_retriever=get_passphrase) - return keystore.list() \ No newline at end of file + return keystore.list() diff --git a/config/docker/config.ini b/config/docker/config.ini new file mode 100644 index 0000000..43d6b22 --- /dev/null +++ b/config/docker/config.ini @@ -0,0 +1,27 @@ +[cic_core] +meta_writer = cic.writers.KVWriter +attachment_writer = cic.writers.KVWriter +proof_writer = cic.writers.KVWriter +ext_writer = cic.writers.KVWriter + +[cic] +registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 + +[meta] +url = http://localhost:63380 +http_origin = + +[rpc] +provider = http://localhost:63545 + +[auth] +type = gnupg +keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc +passphrase = merman + +[wallet] +key_file = /home/will/.config/cic/keystore +passphrase = + +[chain] +spec = evm:byzantium:8996:bloxberg \ No newline at end of file diff --git a/config/dev-docker/config.ini b/config/local/config.ini similarity index 90% rename from config/dev-docker/config.ini rename to config/local/config.ini index 3366663..ce52f19 100644 --- a/config/dev-docker/config.ini +++ b/config/local/config.ini @@ -8,11 +8,11 @@ ext_writer = cic.writers.KVWriter registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 [meta] -url = http://localhost:63380 +url = http://localhost:8000 http_origin = [rpc] -provider = http://localhost:63545 +provider = http://localhost:8545 [auth] type = gnupg diff --git a/config/test-net/config.ini b/config/mainnet/config.ini similarity index 82% rename from config/test-net/config.ini rename to config/mainnet/config.ini index 4bc766c..7429377 100644 --- a/config/test-net/config.ini +++ b/config/mainnet/config.ini @@ -5,14 +5,14 @@ proof_writer = cic.writers.KVWriter ext_writer = cic.writers.KVWriter [cic] -registry_address = 0x86c616fd2f020289d6fd7a98f3bd7539b335c995 +registry_address = 0x999487d8B1EC2b2ac9F4a9D1A6D35a24D75D7EF4 [meta] url = https://meta.grassecon.net http_origin = [rpc] -provider = https://rpc.grassecon.net +provider = https://rpc.kitabu.grassecon.net [auth] type = gnupg diff --git a/config/testnet/config.ini b/config/testnet/config.ini new file mode 100644 index 0000000..e46d3d5 --- /dev/null +++ b/config/testnet/config.ini @@ -0,0 +1,27 @@ +[cic_core] +meta_writer = cic.writers.KVWriter +attachment_writer = cic.writers.KVWriter +proof_writer = cic.writers.KVWriter +ext_writer = cic.writers.KVWriter + +[cic] +registry_address = 0xcf60ebc445b636a5ab787f9e8bc465a2a3ef8299 + +[meta] +url = http://localhost:6700 +http_origin = + +[rpc] +provider = https://rpc.grassecon.net + +[auth] +type = gnupg +keyfile_path = /home/will/grassroots/cic-internal-integration/apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc +passphrase = merman + +[wallet] +key_file = /home/will/grassroots/cic-internal-integration/apps/contract-migration/keystore +passphrase = + +[chain] +spec = evm:byzantium:5050:bloxberg \ No newline at end of file -- 2.45.2